go项目学习二 | 青训营笔记

67 阅读2分钟

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

需求描述

  1. 实现一个展示话题(标题,文字描述)和回帖列表的后端http接口
  2. 暂时不考虑前端页面实现,仅仅实现一个本地web服务
  3. 话题和回帖数据使用本地文件来存储

ER关系

  • |================|......................|===============|
  • |----------Topic----------- |......................|-----------Post----------|
  • |------- id -----------------|......................|------- id ---------------|
  • |------- title -------------- |.......................|-------topic id ---------|
  • |------- content -----------|......................|------- content -------- |
  • |------- create_time -------|......................|------- create_time-----|

分层结构

  • 数据层(Repository): 数据Model,外部数据的增删改查
  • 逻辑层(Service): 业务Entity,处理核心业务逻辑输出,不关心底层的数据存储
  • 视图层(Controller): 视图View,处理和外部的交互逻辑,对上游负责,包装一些数据格式

组件工具

索引设计

var (
	topicIndexMap map[int64]*Topic  //topic索引
	postIndexMap  map[int64][]*Post //发布索引
)

初始化topic数据索引

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)  //生成map
	for scanner.Scan() { //以迭代器的方式进行遍历
		text := scanner.Text()  //获取下一个数据
		var topic Topic
		if err := json.Unmarshal([]byte(text), &topic); err != nil { //使用json包来进行反序列化
			return err
		}
		topicTmpMap[topic.Id] = &topic
	}
	topicIndexMap = topicTmpMap
	return nil  //返回无错误
}

初始化post数据索引

func initPostIndexMap(filePath string) error{
	open, err := os.Open(filePath + "post")
	if err != nil {
		return err
	}
	scanner := bufio.NewScanner(open)
	postTmpMap := make(map[int64][]*Post)
	for scanner.Scan() {
		text := scanner.Text()
		var post Post
		if err := json.Unmarshal([]byte(text), &post); err != nil {
			return err
		}
		posts, ok := postTmpMap[post.ParentId]
		if !ok {
			postTmpMap[post.ParentId] = []*Post{&post}
			continue
		}
		posts = append(posts, &post)
		postTmpMap[post.ParentId] = posts
	}
	postIndexMap = postTmpMap
	return nil
}

查询操作

Dao的初始化

type TopicDao struct {
}
var (
	topicDao  *TopicDao
	topicOnce sync.Once
)
func NewTopicDaoInstance() *TopicDao {
	//sync.Once : 高并发场景下只执行一次  单例模式
	topicOnce.Do(
		func() {
			topicDao = &TopicDao{}
		})
	return topicDao
}

Service层

实体

type PageInfo struct {  //页面信息
	Topic    *repository.Topic
	PostList []*repository.Post
}

type QueryPageInfoFlow struct {  //控制信息流转
	topicId  int64
	pageInfo *PageInfo

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

流程

参数校验

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层

实体构造

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,
	}

}

搭建逻辑框架

  • 初始化数据索引
  • 初始化引擎配置
  • 构建路由
  • 启动服务
    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
	}
}