Go简单项目实战|青训营笔记

86 阅读2分钟

这是我参与「第五届青训营 」笔记创作活动的第4天

需求背景

需要实现一个单页面的服务端内容,仿照的是掘金上帖子页面的功能

社区话题页面

  • 展示话题(标题,文字描述)和回帖列表
  • 不考虑前端功能的实现,仅实现本地web服务
  • 话题和回帖数据用文件存储,暂不使用数据库

用例分析

  • user可以浏览Topic和PostList
  • ER图

image.png

分层结构(MVC)

image.png

具体实现

  • 项目使用gin框架
 go get -u github.com/gin-gonic/gin
  • 我们从下到上进行实现 数据层-逻辑层-视图层
  • 我们先在项目源文件下建立data文件夹,在里面放topic和post文件,建议直接将课程中给的数据贴过来,后面就不用自己编造数据了
  • 以topic为例,post的处理方式相同

Repository

  • 先根据ER图建立topic的实体
```
type Topic struct { //根据ER图创建实体
   Id         int64  `json:"id"`
   Title      string `json:"title"`
   Content    string `json:"content"`
   CreateTime int64  `json:"create_time"`
}
```
  • 再通过sync.Once创建一个单例的实体,并写一个通过id查询topic的方法
type TopicDao struct {
}
var (
	topicDao  *TopicDao
	topicOnce sync.Once
)
func NewTopicDaoInstance() *TopicDao {
	topicOnce.Do(
		func() {
			topicDao = &TopicDao{}
		})
	return topicDao
}
func (*TopicDao) QueryTopicById(id int64) *Topic {
	return topicIndexMap[id]
}
  • 写把数据从文件读到内存的方法
```
func initTopicIndexMap(filePath string) error {
   open, err := os.Open(filePath + "topic")//打开文件
   if err != nil {
      return err
   }
   scanner := bufio.NewScanner(open)
   topicTmpMap := make(map[int64]*Topic)
   for scanner.Scan() {
      text := scanner.Text()
      var topic Topic
      if err := json.Unmarshal([]byte(text), &topic); err != nil {//反序列化
         return err
      }
      topicTmpMap[topic.Id] = &topic
   }
   topicIndexMap = topicTmpMap
   return nil
}
```

Service

  • 采用并行de方法读取topic和post
  • 对数据做简单的校验
```
package service

import (
   "errors"
   "qxy/simple_project/repository"
   "sync"
)

type PageInfo struct {
   Topic    *repository.Topic
   PostList []*repository.Post
}

func QueryPageInfo(topicId int64) (*PageInfo, error) {
   return NewQueryPageInfoFlow(topicId).Do()
}

func NewQueryPageInfoFlow(topId int64) *QueryPageInfoFlow {
   return &QueryPageInfoFlow{
      topicId: topId,
   }
}

type QueryPageInfoFlow struct {
   topicId  int64
   pageInfo *PageInfo

   topic *repository.Topic
   posts []*repository.Post
}

func (f *QueryPageInfoFlow) Do() (*PageInfo, error) {//简单的校验,实际开发中需要做更多完整的校验
   if err := f.checkParam(); err != nil {return nil, err}
   if err := f.prepareInfo(); err != nil {
      return nil, err
   }
   if err := f.packPageInfo(); err != nil {
      return nil, err
   }
   return f.pageInfo, nil
}

func (f *QueryPageInfoFlow) checkParam() error {
   if f.topicId <= 0 {
      return errors.New("topic id must be larger than 0")
   }
   return nil
}

func (f *QueryPageInfoFlow) prepareInfo() error {//并发获取
   //获取topic信息
   var wg sync.WaitGroup
   wg.Add(2)
   go func() {
      defer wg.Done()
      topic := repository.NewTopicDaoInstance().QueryTopicById(f.topicId)
      f.topic = topic
   }()
   //获取post列表
   go func() {
      defer wg.Done()
      posts := repository.NewPostDaoInstance().QueryPostsByParentId(f.topicId)
      f.posts = posts
   }()
   wg.Wait()
   return nil
}

func (f *QueryPageInfoFlow) packPageInfo() error {
   f.pageInfo = &PageInfo{
      Topic:    f.topic,
      PostList: f.posts,
   }
   return nil
}
```

Controller

  • 构建View对象
```
package cotroller

import (
   "strconv"

   "github.com/Moonlight-Zhao/go-project-example/service"
)

type PageData struct {
   Code int64       `json:"code"` //统一返回码
   Msg  string      `json:"msg"`  //相关信息
   Data interface{} `json:"data"` //数据
}

func QueryPageInfo(topicIdStr string) *PageData {
   topicId, err := strconv.ParseInt(topicIdStr, 10, 64) //数据转换
   if err != nil {
      return &PageData{
         Code: -1,
         Msg:  err.Error(),
      }
   }
   pageInfo, err := service.QueryPageInfo(topicId)
   if err != nil {
      return &PageData{
         Code: -1,
         Msg:  err.Error(),
      }
   }
   return &PageData{
      Code: 0,
      Msg:  "success",
      Data: pageInfo,
   }

}
```

配置路由并初始化

```
package main

import (
   "gopkg.in/gin-gonic/gin.v1"
   "os"
   "qxy/simple_project/cotroller"
   "qxy/simple_project/repository"
)

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)
   })
   err := r.Run()
   if err != nil {
      return
   }
}

func Init(filePath string) error {
   if err := repository.Init(filePath); err != nil {
      return err
   }
   return nil
}
```

运行测试

image.png