Hertz 社区话题页面后端 | 青训营

77 阅读3分钟

实践是学习 Golang 后端开发必不可少的步骤,那么本文介绍青训营GO语言工程实践课的一个课后作业:社区话题页面。

需求分析

描述

社区话题页面

  • 展示话题(标题、文字描述)和回帖列表
  • 不考虑前端页面的实现,仅实现一个本地web服务
  • 话题和回帖数据用文件存储
  • 支持发布帖子:本地id生成需要保证不重复,唯一性 append 文件,更新索引

需求用例

用例图.png

E-R 图

erDiagram
    Topic ||--o{ Post:""
    Topic {
        string id
        string title
        string content
        int64 create_time
    }
    Post {
        string id
        string title
        string content
        int64 create_time
    }

分层结构

structure.png

  • 数据层:数据模型,对数据进行增删查改。
  • 逻辑层:业务实体,处理核心业务逻辑输出。
  • 控制层,展现视图,处理和外部的交互逻辑。

实现

代码结构

考虑用 Hertz 进行实现。

先用命令行工具 hz 生成基础代码:

mkdir community_topic-backend
cd community_topic-backend

hz new -module community_topic_backend

那么生成的模板代码里 biz/handler 对应分层中的 controller,也就是响应用户请求, biz/service 对应 service 中的具体实现, biz/dal 对应分层中的dal,即数据的具体访问与存储。

具体代码结构:

❯ tree
.
├── biz
│   ├── dal
│   │   ├── db_init.go
│   │   ├── post.go
│   │   └── topic.go
│   ├── handler
│   │   ├── publish_post.go
│   │   └── query_page_info.go
│   ├── router
│   │   └── register.go
│   └── service
│       ├── publish_post.go
│       └── query_page_info.go
├── build.sh
├── data
│   ├── post
│   └── topic
├── go.mod
├── go.sum
├── main.go
├── output
│   ├── bin
│   │   └── community_topic_backend
│   └── bootstrap.sh
├── router.go
├── router_gen.go
└── script
    └── bootstrap.sh

10 directories, 20 files

分析

路由定义在最上层的 router.go 里:

// Code generated by hertz generator.

package main

import (
	handler "community_topic_backend/biz/handler"

	"github.com/cloudwego/hertz/pkg/app/server"
)

// customizeRegister registers customize routers.
func customizedRegister(r *server.Hertz) {
	r.GET("/community/page/get/:id", handler.QueryPageInfoHandler)
	r.POST("/community/post/do", handler.PublishPostHandler)
}

查询页面帖子对应 biz/handlerbiz/service 里的 query_page_info.go

发帖对应 biz/handlerbiz/service 里的 publish_post.go

biz/dal/db_init.go 定义话题(topic)和话题对应帖子(post)的全局字典存储结构,topicIndexMap map[int64]*Topic postIndexMap map[int64][]*Post,并使用sync.Mutex锁机制保证两个变量使用的并发安全; biz/dal/topic.gobiz/dal/post.go 定义 topic 和 post 实体的数据结构及方法。

将data中的话题和帖子数据分别读取并存储到两个字典中便于使用。

初始化 http 服务、路由等位于 main.go

// Code generated by hertz generator.

package main

import (
	"community_topic_backend/biz/dal"
	"os"

	"github.com/cloudwego/hertz/pkg/app/server"
)

func main() {
	if err := Init("./data/"); err != nil {
		os.Exit(-1)
	}
	h := server.Default()

	register(h)
	h.Spin()
}

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

运行测试:

编译:

./build.sh

测试:

获取 page 的话题列表

❯ curl http://127.0.0.1:8888/community/page/get/1
{"code":0,"msg":"success","data":{"Topic":{"id":1,"title":"青训营来啦!","content":"小姐姐,快到碗里来~","create_time":1650437625},"PostList":[{"id":1,"parent_id":1,"content":"小姐姐快来1","create_time":1650437616},{"id":2,"parent_id":1,"content":"小姐姐快来2","create_time":1650437617},{"id":3,"parent_id":1,"content":"小姐姐快来3","create_time":1650437618},{"id":4,"parent_id":1,"content":"小姐姐快来4","create_time":1650437619},{"id":5,"parent_id":1,"content":"小姐姐快来5","create_time":1650437620}]}}%

上传

 curl -X POST -F 'topic_id=2' -F 'content="Test content for uploading"' http://127.0.0.1:8888/community/post/do
 
 cat ./data/post
 
 {"id":1,"parent_id":1,"content":"小姐姐快来1","create_time":1650437616}
{"id":2,"parent_id":1,"content":"小姐姐快来2","create_time":1650437617}
{"id":3,"parent_id":1,"content":"小姐姐快来3","create_time":1650437618}
{"id":4,"parent_id":1,"content":"小姐姐快来4","create_time":1650437619}
{"id":5,"parent_id":1,"content":"小姐姐快来5","create_time":1650437620}
{"id":6,"parent_id":2,"content":"小哥哥快来1","create_time":1650437621}
{"id":7,"parent_id":2,"content":"小哥哥快来2","create_time":1650437622}
{"id":8,"parent_id":2,"content":"小哥哥快来3","create_time":1650437623}
{"id":9,"parent_id":2,"content":"小哥哥快来4","create_time":1650437624}
{"id":10,"parent_id":2,"content":"小哥哥快来5","create_time":1650437625}
{"id":2879109067299483648,"parent_id":2,"content":"Test content for uploading","create_time":1693063966}

参考

go-project-example

hertz