这是我参与「第五届青训营 」笔记创作活动的第4天
需求背景
需要实现一个单页面的服务端内容,仿照的是掘金上帖子页面的功能
社区话题页面
- 展示话题(标题,文字描述)和回帖列表
- 不考虑前端功能的实现,仅实现本地web服务
- 话题和回帖数据用文件存储,暂不使用数据库
用例分析
- user可以浏览Topic和PostList
- ER图
分层结构(MVC)
具体实现
- 项目使用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
}
```