需求描述
社区话题页面:
- 展示话题(标题,文字描述)和回帖列表
- 实现本地web服务
- 话题和回帖数据用文件存储
需求用例
游览消费用户
- 话题
- 帖子
ER图-Entity Relationship Diagram
展示概念模型的工具,展示相关的属性和关系,为开发提供清晰的思路
Topic:(话题)
- id
- title
- content
- create_time
Post:(帖子)
- id
- topic_id
- content
- create_time
Topic和Post的关系为一对多,一个Topic可以对应多个Post
分层结构
- 数据层:数据Model,外部数据的增删改查。可以屏蔽下游的数据差异,对下游的接口是可以不发生改变的,下游的service层只需要拿到接口即可
- 逻辑层:业务Entity,处理核心业务逻辑输出。通过接收的数据做打包封装,输出实体
- 视图层:视图view,处理和外部的交互逻辑
组件工具
- Gin高性能go web框架github.com/gin-gonic/g…
- Go Mod 使用go mod init初始化依赖管理文件,然后执行go get,gopkg.in/gin-gonic/gin.v1@v1.3.0
Repository
- 对于Topic需要使用QueryTopicById,通过话题的id查询话题
- 对于Post需要使用QueryPostsByParentId,通过话题的id查询所有与这个话题id相关的所有信息
index
index索引概念 来自:索引-识典百科 (baike.com)
定义:
索引是为了加速对表中数据行的检索而创建的一种分散的存储结构。索引是针对表而建立的,它是由数据页面以外的索引页面组成的,每个索引页面中的行都会含有逻辑指针,以便加速检索物理数据。 在数据库关系图中,可以在选定表的“索引键”属性页中创建、编辑或删除每个索引类型。当保存索引所附加到的表,或保存该表所在的关系图时,索引将保存在数据库中。
作用:
在数据库系统中建立索引主要有以下作用:
(1)快速取数据;
(2)保证数据记录的唯一性;
(3)实现表与表之间的参照完整性;
(4)在使用ORDER by、group by子句进行数据检索时,利用索引可以减少排序和分组的时间。
优缺点:
优点
1.大大加快数据的检索速度;
2.创建唯一性索引,保证数据库表中每一行数据的唯一性;
3.加速表和表之间的连接;
4.在使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间。
缺点
1.索引需要占物理空间。
2.当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度。
初始化话题数据索引
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
}
查询
type Topic struct {
Id int64 `json:"id"`
Title string `json;"title"`
Content string `json:"content"`
CreateTime int64 `json:"create_time"`
}
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]
}
Service
实体
type PageInfo struct {
Topic *respositor.Topic
PostList []*respository.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) prepareInfo() error{
//获取topic信息
var wg sync.WaitGroup
wg.Add(2)
go func() {...}()
//获取post列表
go func() {...}()
wg.Wait()
return nil
}
Controller
- 构建view对象
- 业务错误码
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{...}
}
pageInfo,err := service.QueryPageInfo(topicId)
if err != nil {
return &PageData{...}
}
return &PageData{...}
}
Router
- 初始化数据索引
- 舒适化引擎配置
- 构建路由
- 启动服务
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
}
}
小结
本篇文章的代码均来自于后端基础课Go 语言工程实践之测试 - 掘金 (juejin.cn) 还是有很多地方不太理解的,因为自己也是没有任何相关的基础知识,所以学习起来会有点吃力,现在主要是了解了相关的流程知识,有一个大概的认知,不会毫无头绪了。从分析需求开始一步一步直到可以写完整个程序并且运行起来,也是学到了很多知识,有些代码还是不太理解,以后还得慢慢琢磨