├── README.md
├── bootstrap
├── config
│ └── config.go
├── connection
│ └── mysql.go
└── logger
│ └── logger.go
├── build.sh
├── cmd
├── swagger-gen-model
│ └── swagger-gen-model.sh
├── swaggereditor
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── index.html
│ ├── swagger-editor-bundle.js
│ ├── swagger-editor-bundle.js.map
│ ├── swagger-editor-standalone-preset.js
│ ├── swagger-editor-standalone-preset.js.map
│ ├── swagger-editor.css
│ ├── swagger-editor.css.map
│ ├── swagger-editor.js
│ ├── swagger-editor.js.map
│ ├── validation.worker.js
│ └── validation.worker.js.map
└── swaggerui
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── index.html
│ ├── oauth2-redirect.html
│ ├── swagger-ui-bundle.js
│ ├── swagger-ui-bundle.js.map
│ ├── swagger-ui-standalone-preset.js
│ ├── swagger-ui-standalone-preset.js.map
│ ├── swagger-ui.css
│ ├── swagger-ui.css.map
│ ├── swagger-ui.js
│ └── swagger-ui.js.map
├── control.sh
├── controller
├── comment
│ └── api.go
├── knowledge
│ └── api.go
├── tag
│ └── api.go
└── topic
│ └── api.go
├── env.example.yaml
├── env.test.yaml
├── env.yjf.yaml
├── go.mod
├── go.sum
├── main.go
├── middleware
├── access.go
├── auth.go
├── cors.go
└── panic.go
├── model
├── comments.go
├── daily.go
├── db.sql
├── likables.go
├── taggables.go
├── tags.go
├── topics.go
└── users.go
├── storage
└── log
│ ├── .gitkeeper
│ └── hade.log
├── swagger.yaml
├── tester
├── coverage.sh
├── env
│ └── .gitkeeper
├── readme.md
├── report
│ └── .gitkeeper
└── suite
│ ├── suite1
│ ├── after.go
│ ├── before.go
│ ├── case_normal_1.go
│ ├── case_topic.go
│ ├── global.go
│ └── run_test.go
│ └── suite2
│ ├── after.go
│ ├── before.go
│ ├── case_normal_1.go
│ ├── global.go
│ └── run_test.go
├── util
├── debug.go
├── file.go
├── file_test.go
├── folder.go
├── folder_test.go
├── response.go
├── time.go
└── trace.go
└── view
├── adapter
├── comment.go
├── comment_test.go
├── daily.go
├── tag.go
├── topic.go
└── user.go
└── swagger
└── models
├── comment_detail.go
├── comment_summary.go
├── comments.go
├── daily.go
├── daily_content_items.go
├── error_response.go
├── get_tag_topics_o_k_body.go
├── get_topic_list_o_k_body.go
├── pager.go
├── tag.go
├── tags.go
├── topic.go
├── topic_summary.go
├── topic_summarys.go
└── user_summary.go
/README.md:
--------------------------------------------------------------------------------
1 | # backend
2 | 网站的api服务
3 |
--------------------------------------------------------------------------------
/bootstrap/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import(
4 | "github.com/spf13/viper"
5 | )
6 |
7 | // Default 是默认的配置
8 | var Default *viper.Viper
9 |
10 | // Init 初始化加载Config
11 | func Init(config string) error {
12 | Default = viper.New()
13 | Default.SetConfigType("yaml")
14 | Default.SetConfigFile(config)
15 | if err := Default.ReadInConfig(); err != nil {
16 | return err
17 | }
18 | return nil
19 | }
--------------------------------------------------------------------------------
/bootstrap/connection/mysql.go:
--------------------------------------------------------------------------------
1 | package connection
2 |
3 | import (
4 | "github.com/spf13/viper"
5 |
6 | "github.com/jinzhu/gorm"
7 | _ "github.com/jinzhu/gorm/dialects/mysql"
8 | "fmt"
9 | )
10 |
11 | // Default 获取默认的DB
12 | var Default *gorm.DB
13 |
14 | // Init 初始化数据库
15 | func Init(vconf *viper.Viper) error {
16 | username := vconf.GetString("mysql.username")
17 | password := vconf.GetString("mysql.password")
18 | host := vconf.GetString("mysql.host")
19 | port := vconf.GetString("mysql.port")
20 | database := vconf.GetString("mysql.database")
21 | db, err := gorm.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true", username, password, host, port, database))
22 | if err != nil {
23 | return err
24 | }
25 |
26 | db.LogMode(true)
27 | Default = db
28 | return nil
29 | }
30 |
31 | // Refresh 更新数据库
32 | func Refresh(vconf *viper.Viper) error {
33 | Destory(vconf)
34 | return Init(vconf)
35 | }
36 |
37 | // Destory 关掉数据库
38 | func Destory(vconf *viper.Viper) {
39 | Default.Close()
40 | }
--------------------------------------------------------------------------------
/bootstrap/logger/logger.go:
--------------------------------------------------------------------------------
1 | package logger
2 |
3 | import (
4 | "backend/util"
5 | "github.com/sirupsen/logrus"
6 | "github.com/spf13/viper"
7 | "os"
8 | "path"
9 | )
10 |
11 | var Default *logrus.Logger
12 |
13 | // Init 根据配置文件初始化log
14 | func Init(vconf *viper.Viper) error {
15 | folder := vconf.GetString("log.folder")
16 | if folder == "" {
17 | folder = util.LogFolder()
18 | }
19 | Default = logrus.New()
20 | logFile := path.Join(folder, "hade.log")
21 | fd, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
22 | if err != nil {
23 | return err
24 | }
25 | Default.Out = fd
26 | return nil
27 | }
28 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | GOOS=linux GOARCH=amd64 go build -o backend ./
--------------------------------------------------------------------------------
/cmd/swagger-gen-model/swagger-gen-model.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | swagger generate model --spec=swagger.yaml --target=view/swagger --with-flatten=full
--------------------------------------------------------------------------------
/cmd/swaggereditor/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huoding-fullstacks/backend/3955cf6dc76e75af40c985bff03677e5c94eeddd/cmd/swaggereditor/favicon-16x16.png
--------------------------------------------------------------------------------
/cmd/swaggereditor/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huoding-fullstacks/backend/3955cf6dc76e75af40c985bff03677e5c94eeddd/cmd/swaggereditor/favicon-32x32.png
--------------------------------------------------------------------------------
/cmd/swaggereditor/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Swagger Editor
7 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
65 |
66 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/cmd/swaggereditor/swagger-editor.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"swagger-editor.css","sourceRoot":""}
--------------------------------------------------------------------------------
/cmd/swaggerui/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huoding-fullstacks/backend/3955cf6dc76e75af40c985bff03677e5c94eeddd/cmd/swaggerui/favicon-16x16.png
--------------------------------------------------------------------------------
/cmd/swaggerui/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huoding-fullstacks/backend/3955cf6dc76e75af40c985bff03677e5c94eeddd/cmd/swaggerui/favicon-32x32.png
--------------------------------------------------------------------------------
/cmd/swaggerui/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Swagger UI
7 |
8 |
9 |
10 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/cmd/swaggerui/oauth2-redirect.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
68 |
--------------------------------------------------------------------------------
/cmd/swaggerui/swagger-ui.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"swagger-ui.css","sourceRoot":""}
--------------------------------------------------------------------------------
/control.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | chmod o+x backend
4 | killall backend
5 | nohup ./backend --config=env.production.yaml >> storage/log/hade.log &
--------------------------------------------------------------------------------
/controller/comment/api.go:
--------------------------------------------------------------------------------
1 | package comment
2 |
3 | import (
4 | "backend/bootstrap/connection"
5 | "backend/model"
6 | "backend/util"
7 | "backend/view/adapter"
8 | "errors"
9 | "github.com/gin-gonic/gin"
10 | "strconv"
11 | )
12 |
13 | func Register(router *gin.RouterGroup) {
14 | router.POST("comment/create", Create)
15 | router.POST("comment/delete", Delete)
16 | router.GET("comment/list", List)
17 | router.POST("comment/append", Append)
18 | }
19 |
20 | /// 创建评论
21 | func Create(c *gin.Context) {
22 | topicId, _ := strconv.ParseInt(c.DefaultPostForm("topic_id", "0"), 10, 64)
23 | if topicId == 0 {
24 | util.AbortError(c, 400, errors.New("topicId不能为空"))
25 | return
26 | }
27 |
28 | content, exist := c.GetPostForm("content")
29 | if !exist || content == "" {
30 | util.AbortError(c, 40001, errors.New("内容不能为空"))
31 | return
32 | }
33 |
34 | // TODO: 接入登陆以后获取当前用户
35 | meId := int64(1)
36 |
37 | comment := model.Comment{
38 | Content: content,
39 | ParentID: 0,
40 | Status: model.COMMENT_STATUS_REVIEWED,
41 | TopicID: topicId,
42 | UserID: meId,
43 | }
44 | if err := connection.Default.Save(&comment).Error; err != nil {
45 | util.AbortUnknownError(c, err)
46 | return
47 | }
48 |
49 | out := adapter.ToCommentSummary(comment, nil)
50 | util.ResponseSuccess(c, out)
51 | }
52 |
53 | // 软删除评论
54 | func Delete(c *gin.Context) {
55 | commentId, _ := strconv.ParseInt(c.DefaultQuery("comment_id", "0"), 10, 64)
56 | if commentId == 0 {
57 | util.AbortError(c, 40001, errors.New("评论唯一标示不能为空"))
58 | return
59 | }
60 |
61 | comment, err := model.GetComment(commentId)
62 | if err != nil {
63 | util.AbortUnknownError(c, err)
64 | return
65 | }
66 |
67 | if comment == nil {
68 | util.ResponseSuccess(c, nil)
69 | return
70 | }
71 |
72 | if err := model.DeleteComment(commentId); err != nil {
73 | util.AbortUnknownError(c, err)
74 | return
75 | }
76 | util.ResponseSuccess(c, nil)
77 | }
78 |
79 | // 显示评论
80 | func List(c *gin.Context) {
81 | topicId, _ := strconv.ParseInt(c.DefaultQuery("topic_id", "0"), 10, 64)
82 | if topicId == 0 {
83 | util.AbortError(c, 400, errors.New("topicId不能为空"))
84 | return
85 | }
86 |
87 | // 获取所有的评论列表
88 | comments, err := model.GetTopicComments(topicId)
89 | if err != nil {
90 | util.AbortUnknownError(c, err)
91 | return
92 | }
93 |
94 | out, err := adapter.ToComments(comments)
95 | if err != nil {
96 | util.AbortUnknownError(c, err)
97 | return
98 | }
99 |
100 | util.ResponseSuccess(c, out)
101 | }
102 |
103 | func Append(c *gin.Context) {
104 | commentId, _ := strconv.ParseInt(c.DefaultPostForm("comment_id", "0"), 10, 64)
105 | if commentId == 0 {
106 | util.AbortError(c, 400, errors.New("commentId不能为空"))
107 | return
108 | }
109 |
110 | content, exist := c.GetPostForm("content")
111 | if !exist || content == "" {
112 | util.AbortError(c, 40001, errors.New("内容不能为空"))
113 | return
114 | }
115 |
116 | // 判断评论是否存在,且为一级评论
117 | comment, err := model.GetComment(commentId)
118 | if err != nil {
119 | util.AbortUnknownError(c, err)
120 | return
121 | }
122 | if comment.ParentID != 0 {
123 | util.AbortError(c, 40001, errors.New("评论不是一级评论,不能追加"))
124 | return
125 | }
126 |
127 | // TODO: 接入登陆以后获取当前用户
128 | meId := int64(1)
129 |
130 | commentNew := model.Comment{
131 | Content: content,
132 | ParentID: comment.ID,
133 | Status: model.COMMENT_STATUS_REVIEWED,
134 | TopicID: comment.TopicID,
135 | UserID: meId,
136 | }
137 | if err := connection.Default.Save(&commentNew).Error; err != nil {
138 | util.AbortUnknownError(c, err)
139 | return
140 | }
141 |
142 | out := adapter.ToCommentSummary(commentNew, nil)
143 | util.ResponseSuccess(c, out)
144 | }
--------------------------------------------------------------------------------
/controller/knowledge/api.go:
--------------------------------------------------------------------------------
1 | package knowledge
2 |
3 | import (
4 | "backend/model"
5 | "backend/util"
6 | "backend/view/adapter"
7 | "fmt"
8 | "strconv"
9 | "time"
10 |
11 | "github.com/gin-gonic/gin"
12 | "github.com/pkg/errors"
13 | "github.com/sundy-li/html2article"
14 | )
15 |
16 | const (
17 | MpToken = "mp_token"
18 | )
19 |
20 | func Register(router *gin.RouterGroup) {
21 | router.GET("knowledge/daily", Daily)
22 | router.GET("knowledge/list", Lists)
23 | router.POST("knowledge/proxy", Proxy)
24 | }
25 |
26 | // 每日知识点
27 | func Daily(c *gin.Context) {
28 | day := c.DefaultQuery("day", "")
29 | if day == "" {
30 | util.AbortError(c, 500, errors.New("day不能为空"))
31 | return
32 | }
33 | daily, err := model.GetDaily(day)
34 | if err != nil {
35 | util.AbortUnknownError(c, err)
36 | return
37 | }
38 | if daily == nil {
39 | util.AbortError(c, 500, errors.New("没有当日的信息"))
40 | return
41 | }
42 | cont, err := daily.ParseContent()
43 | if err != nil {
44 | util.AbortError(c, 500, errors.New("解析当日信息错误"))
45 | return
46 | }
47 |
48 | ret := adapter.ToDaily(daily, cont)
49 |
50 | util.ResponseSuccess(c, ret)
51 | }
52 |
53 | // 获取某天之前的每日信息
54 | func Lists(c *gin.Context) {
55 | day := c.DefaultQuery("day", "")
56 | count := c.DefaultQuery("count", "5")
57 | token := c.DefaultQuery("token", "")
58 | if token != MpToken {
59 | util.AbortError(c, 500, errors.New("token 错误"))
60 | return
61 | }
62 | if day == "" {
63 | // 使用明天作为
64 | tomorow := time.Now().AddDate(0, 0, 1)
65 | day = tomorow.Format("2006-01-02")
66 | }
67 | cn, _ := strconv.Atoi(count)
68 | dailys, err := model.GetDailyList(day, cn)
69 | if err != nil {
70 | util.AbortUnknownError(c, err)
71 | return
72 | }
73 | util.ResponseSuccess(c, dailys)
74 | }
75 |
76 | type ProxyRequest struct {
77 | URL string `form:"url"`
78 | }
79 |
80 | // 获取某个地址的HTML内容
81 | func Proxy(c *gin.Context) {
82 | token := c.DefaultQuery("token", "")
83 | url := c.PostForm("url")
84 | if token != MpToken {
85 | util.AbortError(c, 500, errors.New("token不对"))
86 | return
87 | }
88 |
89 | if url == "" {
90 | util.AbortError(c, 500, errors.New("url 为空"))
91 | return
92 | }
93 | fmt.Println(url)
94 | ext, err := html2article.NewFromUrl(url)
95 | if err != nil {
96 | util.AbortError(c, 500, errors.New("url返回错误"+err.Error()))
97 | return
98 | }
99 | article, err := ext.ToArticle()
100 | if err != nil {
101 | util.AbortError(c, 500, errors.New(err.Error()))
102 | return
103 | }
104 | article.Readable(url)
105 |
106 | type Article struct {
107 | Title string
108 | PublishTime int64
109 | ReadContent string
110 | }
111 |
112 | art := &Article{
113 | Title: article.Title,
114 | PublishTime: article.Publishtime,
115 | ReadContent: article.ReadContent,
116 | }
117 |
118 | util.ResponseSuccess(c, art)
119 | }
120 |
--------------------------------------------------------------------------------
/controller/tag/api.go:
--------------------------------------------------------------------------------
1 | package tag
2 |
3 | import (
4 | "backend/model"
5 | "backend/util"
6 | "backend/view/adapter"
7 | "backend/view/swagger/models"
8 | "errors"
9 | "github.com/gin-gonic/gin"
10 | "math"
11 | "strconv"
12 | )
13 |
14 | func Register(router *gin.RouterGroup) {
15 | router.GET("/tag/list", List)
16 | router.GET("/tag/topics", Topics)
17 | }
18 |
19 | // 获取话题列表
20 | func Topics(c *gin.Context) {
21 | // 参数验证
22 | size, _ := strconv.ParseInt(c.DefaultQuery("size", "20"), 10, 64)
23 | page, _ := strconv.ParseInt(c.DefaultQuery("page", "1"), 10, 64)
24 | if page == 0 {
25 | util.AbortError(c, 400, errors.New("page不应该为0"))
26 | return
27 | }
28 | tagId, _ := strconv.ParseInt(c.DefaultQuery("tag_id", "20"), 10, 64)
29 |
30 | // 获取数据
31 | var sum int64
32 | offset := (page - 1) * size
33 | topics, sum, err := model.GetTopicsByTagPager(tagId, offset, size)
34 | if err != nil {
35 | util.AbortUnknownError(c, err)
36 | return
37 | }
38 |
39 | // 组织返回数据
40 | retTopics := adapter.ToTopicSummarysByTopicsWithUser(topics)
41 |
42 | totalPage := int64(math.Ceil(float64(sum) / float64(size)))
43 | t1 := int64(page)
44 | t2 := int64(size)
45 | pager := models.Pager{
46 | Page: &t1,
47 | Size: &t2,
48 | TotalPage: &totalPage,
49 | }
50 |
51 | // 返回数据
52 | util.ResponseSuccess(c, models.GetTagTopicsOKBody{Topics: retTopics, Pager: &pager})
53 | }
54 |
55 | func List(c *gin.Context) {
56 | tags, err := model.GetAllTags()
57 | if err != nil {
58 | util.AbortUnknownError(c, err)
59 | }
60 |
61 | out := adapter.ToTags(tags)
62 | util.ResponseSuccess(c, out)
63 | }
--------------------------------------------------------------------------------
/controller/topic/api.go:
--------------------------------------------------------------------------------
1 | package topic
2 |
3 | import (
4 | "backend/bootstrap/connection"
5 | "backend/model"
6 | "backend/util"
7 | "backend/view/adapter"
8 | "backend/view/swagger/models"
9 | "errors"
10 | "github.com/gin-gonic/gin"
11 | "github.com/jianfengye/collection"
12 | "math"
13 | "strconv"
14 | )
15 |
16 | // Register 注册路由
17 | func Register(router *gin.RouterGroup) {
18 | router.GET("/topic/list", List)
19 | router.GET("/topic/detail", Detail)
20 | router.POST("/topic/create", Create)
21 | router.POST("/topic/update", Update)
22 | router.POST("/topic/delete", Delete)
23 | router.POST("/topic/like", Like)
24 | }
25 |
26 | // List 获取话题列表
27 | func List(c *gin.Context) {
28 | // 参数验证
29 | size, _ := strconv.ParseInt(c.DefaultQuery("size", "20"), 10, 64)
30 | page, _ := strconv.ParseInt(c.DefaultQuery("page", "1"), 10, 64)
31 | if page == 0 {
32 | util.AbortError(c, 400, errors.New("page不应该为0"))
33 | return
34 | }
35 |
36 | // 获取数据
37 | var sum int64
38 | offset := (page - 1) * size
39 | topics, sum, err := model.GetTopicsByPager(offset, size)
40 | if err != nil {
41 | util.AbortUnknownError(c, err)
42 | return
43 | }
44 |
45 | // 组织返回数据
46 | retTopics := adapter.ToTopicSummarysByTopicsWithUser(topics)
47 |
48 | totalPage := int64(math.Ceil(float64(sum) / float64(size)))
49 | t1 := int64(page)
50 | t2 := int64(size)
51 | pager := models.Pager{
52 | Page: &t1,
53 | Size: &t2,
54 | TotalPage: &totalPage,
55 | }
56 |
57 | // 返回数据
58 | util.ResponseSuccess(c, models.GetTopicListOKBody{Topics: retTopics, Pager: &pager})
59 | }
60 |
61 | // Detail 获取话题详情
62 | func Detail(c *gin.Context) {
63 | // 参数验证
64 | topicId, _ := strconv.ParseInt(c.DefaultQuery("topic_id", "0"), 10, 64)
65 | if topicId == 0 {
66 | util.AbortError(c, 400, errors.New("topicId不能为空"))
67 | return
68 | }
69 |
70 | topic := model.GetTopicWithUser(topicId)
71 | if topic == nil {
72 | util.AbortError(c, 40001, errors.New("话题不存在"))
73 | return
74 | }
75 |
76 | tags, err := model.GetTopicTags(topicId)
77 | if err != nil {
78 | util.AbortUnknownError(c, err)
79 | return
80 | }
81 |
82 | out := adapter.ToTopicByTopicWithUser(topic, tags)
83 | util.ResponseSuccess(c, out)
84 | }
85 |
86 | // Create 创建话题
87 | func Create(c *gin.Context) {
88 | title, exist := c.GetPostForm("title")
89 | if !exist || title == "" {
90 | util.AbortError(c, 40001, errors.New("标题不能为空"))
91 | return
92 | }
93 | content, exist := c.GetPostForm("content")
94 | if !exist || content == "" {
95 | util.AbortError(c, 40001, errors.New("内容不能为空"))
96 | return
97 | }
98 | link, exist := c.GetPostForm("link")
99 | if !exist {
100 | util.AbortError(c, 40001, errors.New("必须传递link参数,空字符串代表置空"))
101 | return
102 | }
103 | tags, exist := c.GetPostFormArray("tag_ids")
104 | if !exist {
105 | util.AbortError(c, 40001, errors.New("标签必须传递,空代表置空"))
106 | }
107 | tagColl := collection.NewStrCollection(tags)
108 | tagIds, err := tagColl.Map(func(item interface{}, key int) interface{} {
109 | t := item.(string)
110 | i, err := strconv.ParseInt(t, 10, 64)
111 |
112 | if err != nil {
113 | tagColl.SetErr(err)
114 | return nil
115 | }
116 | return i
117 | }).ToInt64s()
118 | if err != nil {
119 | util.AbortUnknownError(c, err)
120 | return
121 | }
122 |
123 | // TODO: 接入登陆以后获取当前用户
124 | meId := int64(1)
125 |
126 | topic := model.Topic{}
127 | topic.Title = title
128 | topic.Content = content
129 | topic.Link = link
130 | topic.Status = model.TOPIC_STATUS_REVIEWED
131 | topic.UserID = meId
132 |
133 | if err := connection.Default.Save(&topic).Error; err != nil {
134 | util.AbortUnknownError(c, err)
135 | return
136 | }
137 |
138 | // 保存话题的topic
139 | if err := model.AddTagsToTopic(topic.ID, tagIds); err != nil {
140 | util.AbortUnknownError(c, err)
141 | return
142 | }
143 |
144 | out := adapter.ToTopicSummary(&topic)
145 | util.ResponseSuccess(c, out)
146 | }
147 |
148 | // Update 更新话题
149 | func Update(c *gin.Context) {
150 | // 参数验证
151 | topicId, _ := strconv.ParseInt(c.DefaultPostForm("topic_id", "0"), 10, 64)
152 | if topicId == 0 {
153 | util.AbortError(c, 400, errors.New("topicId不能为空"))
154 | return
155 | }
156 |
157 | topic := model.GetTopic(topicId)
158 | if topic == nil {
159 | util.AbortError(c, 40001, errors.New("话题不存在"))
160 | return
161 | }
162 |
163 | title, exist := c.GetPostForm("title")
164 | if !exist || title == "" {
165 | util.AbortError(c, 40001, errors.New("标题不能为空"))
166 | return
167 | }
168 | content, exist := c.GetPostForm("content")
169 | if !exist || content == "" {
170 | util.AbortError(c, 40001, errors.New("内容不能为空"))
171 | return
172 | }
173 | link, exist := c.GetPostForm("link")
174 | if !exist {
175 | util.AbortError(c, 40001, errors.New("必须传递link参数,空字符串代表置空"))
176 | return
177 | }
178 | tags, exist := c.GetPostFormArray("tag_ids")
179 | if !exist {
180 | util.AbortError(c, 40001, errors.New("标签必须传递,空代表置空"))
181 | return
182 | }
183 | tagColl := collection.NewStrCollection(tags)
184 | tagIds, err := tagColl.Map(func(item interface{}, key int) interface{} {
185 | t := item.(string)
186 | i, err := strconv.ParseInt(t, 10, 64)
187 |
188 | if err != nil {
189 | tagColl.SetErr(err)
190 | return nil
191 | }
192 | return i
193 | }).ToInt64s()
194 | if err != nil {
195 | util.AbortUnknownError(c, err)
196 | return
197 | }
198 |
199 | topic.Title = title
200 | topic.Content = content
201 | topic.Link = link
202 |
203 | if err := connection.Default.Save(&topic).Error; err != nil {
204 | util.AbortUnknownError(c, err)
205 | return
206 | }
207 |
208 | if err := model.UpdateTopicTags(topicId, tagIds); err != nil {
209 | util.AbortUnknownError(c, err)
210 | return
211 | }
212 |
213 | util.ResponseSuccess(c, nil)
214 | }
215 |
216 | // Delete 删除话题,软删除
217 | func Delete(c *gin.Context) {
218 | // 参数验证
219 | topicId, _ := strconv.ParseInt(c.DefaultPostForm("topic_id", "0"), 10, 64)
220 | if topicId == 0 {
221 | util.AbortError(c, 400, errors.New("topicId不能为空"))
222 | return
223 | }
224 |
225 | topic := model.GetTopic(topicId)
226 | if topic == nil {
227 | util.AbortError(c, 40001, errors.New("话题不存在"))
228 | return
229 | }
230 |
231 | // TODO
232 | meId := int64(1)
233 | if topic.UserID != meId {
234 | util.AbortError(c, 40001, errors.New("你不是话题创建者,不能删除话题"))
235 | }
236 |
237 | if err := model.DeleteTopic(topicId); err != nil {
238 | util.AbortUnknownError(c, err)
239 | return
240 | }
241 |
242 | util.ResponseSuccess(c, nil)
243 | }
244 |
245 | // Like 为某个话题点赞
246 | func Like(c *gin.Context) {
247 | // 参数验证
248 | topicId, _ := strconv.ParseInt(c.DefaultPostForm("topic_id", "0"), 10, 64)
249 | if topicId == 0 {
250 | util.AbortError(c, 400, errors.New("topicId不能为空"))
251 | return
252 | }
253 |
254 | topic := model.GetTopic(topicId)
255 | if topic == nil {
256 | util.AbortError(c, 40001, errors.New("话题不存在"))
257 | return
258 | }
259 |
260 | // TODO: 接入登陆以后获取当前用户
261 | meId := int64(1)
262 |
263 | exist, err := model.CheckTopicLiked(topic.ID, meId)
264 | if err != nil {
265 | util.AbortUnknownError(c, err)
266 | return
267 | }
268 |
269 | if exist {
270 | util.AbortError(c, 40002, errors.New("你已经点赞过了"))
271 | return
272 | }
273 | if err := model.DoTopicLike(topic.ID, meId); err != nil {
274 | util.AbortUnknownError(c, err)
275 | return
276 | }
277 |
278 | util.ResponseSuccess(c, nil)
279 | }
280 |
281 |
--------------------------------------------------------------------------------
/env.example.yaml:
--------------------------------------------------------------------------------
1 | mysql:
2 | host: 192.168.33.10
3 | port: 3306
4 | username: root
5 | password: 2613000
6 | database: huoding
--------------------------------------------------------------------------------
/env.test.yaml:
--------------------------------------------------------------------------------
1 | # 自身的端口设置
2 | app:
3 | ip: 0.0.0.0
4 | port: 8801
5 | mysql:
6 | host: 192.168.33.10
7 | port: 3306
8 | username: root
9 | password: 2613000
10 | database: huoding_test
11 | log:
12 | name: yjf
13 | rotate: 3 day
14 | folder:
15 | err:
16 | mode: show # show/hide,把所有内部错误,错误内容显示在msg中,否则只显示内部错误
--------------------------------------------------------------------------------
/env.yjf.yaml:
--------------------------------------------------------------------------------
1 | # 自身的端口设置
2 | app:
3 | ip: 0.0.0.0
4 | port: 8801
5 | mysql:
6 | host: 192.168.33.10
7 | port: 3306
8 | username: root
9 | password: 2613000
10 | database: huoding
11 | log:
12 | name: yjf
13 | rotate: 3 day
14 | folder:
15 | err:
16 | mode: show # show/hide,把所有内部错误,错误内容显示在msg中,否则只显示内部错误
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module backend
2 |
3 | go 1.12
4 |
5 | replace golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c => github.com/golang/net v0.0.0-20190607181551-461777fb6f67
6 |
7 | replace golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 => github.com/golang/sys v0.0.0-20190608050228-5b15430b70e3
8 |
9 | require (
10 | github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
11 | github.com/gin-gonic/gin v1.4.0
12 | github.com/go-openapi/errors v0.19.2
13 | github.com/go-openapi/strfmt v0.19.0
14 | github.com/go-openapi/swag v0.19.2
15 | github.com/go-openapi/validate v0.19.2
16 | github.com/jianfengye/collection v0.0.0-20190620142820-c4fe771a3171
17 | github.com/jinzhu/gorm v1.9.8
18 | github.com/pkg/errors v0.8.0
19 | github.com/sirupsen/logrus v1.4.2
20 | github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a
21 | github.com/spf13/viper v1.4.0
22 | github.com/sundy-li/html2article v0.0.0-20180131134645-09ac198090c2
23 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect
24 | )
25 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
3 | cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
4 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
5 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
6 | github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
7 | github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
8 | github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
9 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
10 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
11 | github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
12 | github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
13 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
14 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
15 | github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
16 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
17 | github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
18 | github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
19 | github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
20 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
21 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
22 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
23 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
24 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
25 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
26 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
27 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
28 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
29 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
30 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
31 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
32 | github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02 h1:PS3xfVPa8N84AzoWZHFCbA0+ikz4f4skktfjQoNMsgk=
33 | github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
34 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
35 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
36 | github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
37 | github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
38 | github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
39 | github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
40 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
41 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
42 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
43 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
44 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
45 | github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g=
46 | github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
47 | github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
48 | github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
49 | github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
50 | github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
51 | github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
52 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
53 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
54 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
55 | github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
56 | github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
57 | github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
58 | github.com/go-openapi/analysis v0.19.2 h1:ophLETFestFZHk3ji7niPEL4d466QjW+0Tdg5VyDq7E=
59 | github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
60 | github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
61 | github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
62 | github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY=
63 | github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
64 | github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
65 | github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
66 | github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0=
67 | github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
68 | github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
69 | github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
70 | github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
71 | github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
72 | github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
73 | github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
74 | github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
75 | github.com/go-openapi/loads v0.19.2 h1:rf5ArTHmIJxyV5Oiks+Su0mUens1+AjpkPoWr5xFRcI=
76 | github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
77 | github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
78 | github.com/go-openapi/runtime v0.19.0 h1:sU6pp4dSV2sGlNKKyHxZzi1m1kG4WnYtWcJ+HYbygjE=
79 | github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
80 | github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
81 | github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
82 | github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE=
83 | github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
84 | github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
85 | github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
86 | github.com/go-openapi/strfmt v0.19.0 h1:0Dn9qy1G9+UJfRU7TR8bmdGxb4uifB7HNrJjOnV0yPk=
87 | github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
88 | github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
89 | github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
90 | github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE=
91 | github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
92 | github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
93 | github.com/go-openapi/validate v0.19.2 h1:ky5l57HjyVRrsJfd2+Ro5Z9PjGuKbsmftwyMtk8H7js=
94 | github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
95 | github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
96 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
97 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
98 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
99 | github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
100 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
101 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
102 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
103 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
104 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
105 | github.com/golang/net v0.0.0-20190607181551-461777fb6f67/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
106 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
107 | github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
108 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
109 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
110 | github.com/golang/sys v0.0.0-20190608050228-5b15430b70e3 h1:aRnEj+IDKUrLyCuHcsIuRzM4Xsh7TCqji1NIc4DvFDc=
111 | github.com/golang/sys v0.0.0-20190608050228-5b15430b70e3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
112 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
113 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
114 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
115 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
116 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
117 | github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
118 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
119 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
120 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
121 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
122 | github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
123 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
124 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
125 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
126 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
127 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
128 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
129 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
130 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
131 | github.com/jianfengye/collection v0.0.0-20190620142820-c4fe771a3171 h1:uATJtP3TVww0t5le+RPprXm+TqKaIZOV4qoVh6ElR8M=
132 | github.com/jianfengye/collection v0.0.0-20190620142820-c4fe771a3171/go.mod h1:T33ep8mG56L9FRBGzJqdp+Urs6Pa1KHkPvwXRiCbjsA=
133 | github.com/jinzhu/gorm v1.9.8 h1:n5uvxqLepIP2R1XF7pudpt9Rv8I3m7G9trGxJVjLZ5k=
134 | github.com/jinzhu/gorm v1.9.8/go.mod h1:bdqTT3q6dhSph2K3pWxrHP6nqxuAp2yQ3KFtc3U3F84=
135 | github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k=
136 | github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
137 | github.com/jinzhu/now v1.0.0 h1:6WV8LvwPpDhKjo5U9O6b4+xdG/jTXNPwlDme/MTo8Ns=
138 | github.com/jinzhu/now v1.0.0/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc=
139 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
140 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
141 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
142 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
143 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
144 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
145 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
146 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
147 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
148 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
149 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
150 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
151 | github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
152 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
153 | github.com/lib/pq v1.1.0 h1:/5u4a+KGJptBRqGzPvYQL9p0d/tPR4S31+Tnzj9lEO4=
154 | github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
155 | github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
156 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
157 | github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
158 | github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
159 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4=
160 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
161 | github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
162 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
163 | github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
164 | github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
165 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
166 | github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
167 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
168 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
169 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
170 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
171 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
172 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
173 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
174 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
175 | github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
176 | github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
177 | github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
178 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
179 | github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
180 | github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
181 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
182 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
183 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
184 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
185 | github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
186 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
187 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
188 | github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
189 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
190 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
191 | github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
192 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
193 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
194 | github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
195 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
196 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
197 | github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
198 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
199 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
200 | github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
201 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
202 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
203 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
204 | github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
205 | github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
206 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
207 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
208 | github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
209 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
210 | github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
211 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
212 | github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
213 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
214 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
215 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
216 | github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
217 | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
218 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
219 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
220 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
221 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
222 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
223 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
224 | github.com/sundy-li/html2article v0.0.0-20180131134645-09ac198090c2 h1:oKj2rhPl8vQ6SThsvFSuMDEbWCqPyxy/CGCNupPWCfY=
225 | github.com/sundy-li/html2article v0.0.0-20180131134645-09ac198090c2/go.mod h1:qEPne4GSiuwCg1E5EuIjpk+O6ZAMwpDZnzqu1I5WEGU=
226 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
227 | github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw=
228 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
229 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
230 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
231 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
232 | go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
233 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
234 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
235 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
236 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
237 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
238 | golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
239 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
240 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
241 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
242 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
243 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
244 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
245 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
246 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
247 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
248 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
249 | golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
250 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
251 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
252 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
253 | golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
254 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
255 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
256 | golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
257 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
258 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
259 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
260 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
261 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
262 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
263 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
264 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
265 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
266 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
267 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
268 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
269 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
270 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
271 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
272 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
273 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
274 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
275 | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
276 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
277 | golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
278 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
279 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
280 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
281 | golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4=
282 | golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
283 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
284 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
285 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
286 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
287 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
288 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
289 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
290 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
291 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
292 | golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
293 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
294 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
295 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
296 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
297 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
298 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
299 | golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
300 | google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
301 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
302 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
303 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
304 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
305 | google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
306 | google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
307 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
308 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
309 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
310 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
311 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
312 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
313 | gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
314 | gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
315 | gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
316 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
317 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
318 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
319 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
320 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
321 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
322 | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
323 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
324 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
325 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "backend/bootstrap/logger"
5 | "backend/controller/comment"
6 | "backend/controller/knowledge"
7 | "backend/controller/tag"
8 | "backend/controller/topic"
9 | "backend/middleware"
10 | "flag"
11 | "fmt"
12 |
13 | "os"
14 | "path"
15 | "log"
16 |
17 | "backend/bootstrap/connection"
18 | "backend/bootstrap/config"
19 | "backend/util"
20 |
21 | "github.com/gin-gonic/gin"
22 | )
23 |
24 | func main() {
25 | // 读取配置文件
26 | var cf string
27 | flag.StringVar(&cf, "config", "", "config path")
28 | flag.Parse()
29 | if cf == "" {
30 | cf = path.Join(util.RootFolder(), "env.default.yaml")
31 | if !util.FileIsExist(cf) {
32 | flag.PrintDefaults()
33 | os.Exit(1)
34 | }
35 | }
36 |
37 | if err := config.Init(cf); err != nil {
38 | log.Fatalln(err)
39 | }
40 |
41 | // 加载日志
42 | if err := logger.Init(config.Default); err != nil {
43 | log.Fatalln(err)
44 | }
45 |
46 | // 加载数据库
47 | if err := connection.Init(config.Default); err != nil {
48 | log.Fatalln(err)
49 | }
50 | defer connection.Destory(config.Default)
51 |
52 | // 加载路由
53 | r := gin.Default()
54 | r.Use(middleware.CorsMiddleware())
55 | api := r.Group("/api")
56 | {
57 | topic.Register(api)
58 | comment.Register(api)
59 | tag.Register(api)
60 | knowledge.Register(api)
61 | }
62 |
63 | // 启动服务
64 | ip := config.Default.GetString("app.ip")
65 | port := config.Default.GetString("app.port")
66 | addr := fmt.Sprint(ip, ":", port)
67 | if err := r.Run(addr); err != nil {
68 | log.Fatalln(err)
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/middleware/access.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
--------------------------------------------------------------------------------
/middleware/auth.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 |
4 |
--------------------------------------------------------------------------------
/middleware/cors.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | )
6 |
7 | func CorsMiddleware() gin.HandlerFunc {
8 | return func(c *gin.Context) {
9 | c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
10 | c.Next()
11 | c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
12 | }
13 | }
--------------------------------------------------------------------------------
/middleware/panic.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | )
6 |
7 | // RecoveryMiddleware捕获所有panic,并且返回错误信息
8 | func RecoveryMiddleware() gin.HandlerFunc {
9 | return func(c *gin.Context) {
10 | defer func() {
11 | if err := recover(); err != nil {
12 | // TODO: 先做一下日志记录
13 |
14 | // TODO: 返回
15 | return
16 | }
17 | }()
18 | c.Next()
19 | }
20 | }
--------------------------------------------------------------------------------
/model/comments.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "backend/bootstrap/connection"
5 | "github.com/jinzhu/gorm"
6 | "time"
7 | )
8 |
9 | const (
10 | COMMENT_STATUS_REVIEWED = "reviewed"
11 | COMMENT_STATUS_UNREVIEWED = "unreviewed"
12 | COMMNET_STATUS_DELETED = "deleted"
13 | )
14 |
15 | type Comment struct {
16 | Content string `gorm:"column:content" json:"content"`
17 | CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
18 | ID int64 `gorm:"column:id;primary_key" json:"id;primary_key"`
19 | LeftID int64 `gorm:"column:left_id" json:"left_id"`
20 | LikeCount int64 `gorm:"column:like_count" json:"like_count"`
21 | ParentID int64 `gorm:"column:parent_id" json:"parent_id"`
22 | RightID int64 `gorm:"column:right_id" json:"right_id"`
23 | Status string `gorm:"column:status" json:"status"`
24 | TopicID int64 `gorm:"column:topic_id" json:"topic_id"`
25 | UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
26 | UserID int64 `gorm:"column:user_id" json:"user_id"`
27 | User User `gorm:"ForeignKey:user_id"`
28 | }
29 |
30 | // TableName sets the insert table name for this struct type
31 | func (c *Comment) TableName() string {
32 | return "comments"
33 | }
34 |
35 | func GetComment(id int64) (*Comment, error) {
36 | comment := Comment{}
37 | if err := connection.Default.Model(&Comment{}).First(&comment, id).Error; err != nil {
38 | if gorm.IsRecordNotFoundError(err) {
39 | return nil, nil
40 | }
41 | return nil, err
42 | }
43 | return &comment, nil
44 | }
45 |
46 | // 删除一个话题
47 | func DeleteComment(id int64) error {
48 | if err := connection.Default.Model(&Comment{}).Set("status", TOPIC_STATUS_DELETED).Where("id = ?", id).Error; err != nil {
49 | return err
50 | }
51 | return nil
52 | }
53 |
54 |
55 | // 获取话题的标签
56 | func GetTopicComments(topicId int64) ([]Comment, error) {
57 | var comments []Comment
58 | if err := connection.Default.Model(&Comment{}).Where("topic_id = ?", topicId).Where("status = ?", COMMENT_STATUS_REVIEWED).Preload("User").Find(&comments).Error; err != nil {
59 | if gorm.IsRecordNotFoundError(err) {
60 | return nil, nil
61 | }
62 | return nil, err
63 | }
64 | return comments, nil
65 | }
--------------------------------------------------------------------------------
/model/daily.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "backend/bootstrap/connection"
5 | "encoding/json"
6 | "github.com/jinzhu/gorm"
7 | "time"
8 | )
9 |
10 | type Daily struct {
11 | CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
12 | ID int64 `gorm:"column:id;primary_key" json:"id;primary_key"`
13 | Title string `gorm:"column:title" json:"title"`
14 | Author string `gorm:"column:author" json:"author"`
15 | Day string `gorm:"column:day" json:"day"`
16 | Content string `gorm:"column:content" json:"content"`
17 | UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
18 | }
19 |
20 | // Content内的json结构
21 | type Content struct {
22 | Title string `json:"title"`
23 | Link string `json:"link"`
24 | Comment string `json:"comment"`
25 | }
26 |
27 | // TableName sets the insert table name for this struct type
28 | func (d *Daily) TableName() string {
29 | return "daily"
30 | }
31 |
32 | // 某日之前的
33 | func GetDailyList(day string, count int) ([]*Daily, error) {
34 | dailys := []*Daily{}
35 | if err := connection.Default.Model(&Daily{}).Where("day < ?", day).Order("day desc").Limit(count).Find(&dailys).Error; err != nil {
36 | if gorm.IsRecordNotFoundError(err) {
37 | return nil, nil
38 | }
39 | return nil, err
40 | }
41 | return dailys, nil
42 | }
43 |
44 | // 获取每日情况
45 | func GetDaily(day string) (*Daily, error) {
46 | daily := Daily{}
47 | if err := connection.Default.Model(&Daily{}).First(&daily, "day=?", day).Error; err != nil {
48 | if gorm.IsRecordNotFoundError(err) {
49 | return nil, nil
50 | }
51 | return nil, err
52 | }
53 | return &daily, nil
54 | }
55 |
56 | // 解析content中的信息
57 | func (d *Daily) ParseContent() ([]Content, error) {
58 | c := []Content{}
59 | err := json.Unmarshal([]byte(d.Content), &c)
60 | if err != nil {
61 | return c, err
62 | }
63 | return c, nil
64 | }
65 |
--------------------------------------------------------------------------------
/model/db.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE `daily` (
2 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
3 | `title` varchar(100) NOT NULL DEFAULT '' COMMENT '本日知识点',
4 | `author` varchar(30) NOT NULL DEFAULT '' COMMENT '作者',
5 | `day` varchar(30) NOT NULL DEFAULT '' COMMENT '日期,年月日,用横杠分割,比如2019-03-01',
6 | `content` text NOT NULL COMMENT 'json存储的内容,{[title,link,comment]}',
7 | `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
8 | `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`)
9 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='每日知识点';
--------------------------------------------------------------------------------
/model/likables.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "backend/bootstrap/connection"
5 | "errors"
6 | "github.com/jinzhu/gorm"
7 | "time"
8 | )
9 |
10 | type Likeable struct {
11 | CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
12 | ID int64 `gorm:"column:id;primary_key" json:"id;primary_key"`
13 | LikableID int64 `gorm:"column:likable_id" json:"likable_id"`
14 | LikableType string `gorm:"column:likable_type" json:"likable_type"`
15 | UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
16 | UserID int64 `gorm:"column:user_id" json:"user_id"`
17 | }
18 |
19 | // TableName sets the insert table name for this struct type
20 | func (l *Likeable) TableName() string {
21 | return "likables"
22 | }
23 |
24 |
25 | // 话题喜欢查询返回结构
26 | type TopicLike struct {
27 | TopicId int64 `gorm:"column:topic_id" json:"topic_id"`
28 | LikeCount int64 `gorm:"column:like_count" json:"like_count"`
29 | }
30 |
31 | // 获取Topic喜欢个数
32 | func GetTopicLikeCount(topicIds []int64) (map[int64]int64, error) {
33 | if len(topicIds) == 0 {
34 | return nil, nil
35 | }
36 |
37 | var topicLikes []TopicLike
38 | if err := connection.Default.Model(&Likeable{}).Select("count(*) as like_count, likable_id as topic_id").Where("likable_type = ?", "topic").Where("likable_id in (?)", topicIds).Group("likable_id").Scan(&topicLikes).Error; !gorm.IsRecordNotFoundError(err){
39 | return nil, err
40 | }
41 | out := make(map[int64]int64, len(topicLikes))
42 | for _, tl := range topicLikes {
43 | out[tl.TopicId] = tl.LikeCount
44 | }
45 | return out, nil
46 | }
47 |
48 | // 某人是否已经喜欢过某个话题
49 | func CheckTopicLiked(topicId int64, userId int64) (bool, error) {
50 | if userId == 0 || topicId == 0 {
51 | return false, errors.New("参数错误")
52 | }
53 |
54 | var count int
55 | if err := connection.Default.Model(&Likeable{}).Where("user_id = ?", userId).Where("likable_id = ?", topicId).Where("likable_type = ?", "topic").Count(count).Error; err != nil {
56 | return false ,err
57 | }
58 |
59 | if count > 0 {
60 | return true, nil
61 | }
62 |
63 | return false, nil
64 | }
65 |
66 | // 某人点赞某个话题
67 | func DoTopicLike(topicId, userId int64) error {
68 | if userId == 0 || topicId == 0 {
69 | return errors.New("参数错误")
70 | }
71 |
72 | topicLike := Likeable{
73 | LikableID: topicId,
74 | LikableType: "topic",
75 | UserID: userId,
76 | }
77 | if err := connection.Default.Create(&topicLike).Error; err != nil {
78 | return err
79 | }
80 | return nil
81 | }
--------------------------------------------------------------------------------
/model/taggables.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "time"
4 |
5 | type Taggable struct {
6 | CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
7 | ID int64 `gorm:"column:id;primary_key" json:"id;primary_key"`
8 | TagID int64 `gorm:"column:tag_id" json:"tag_id"`
9 | TaggableID int64 `gorm:"column:taggable_id" json:"taggable_id"`
10 | TaggableType string `gorm:"column:taggable_type" json:"taggable_type"`
11 | UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
12 | }
13 |
14 | // TableName sets the insert table name for this struct type
15 | func (t *Taggable) TableName() string {
16 | return "taggables"
17 | }
--------------------------------------------------------------------------------
/model/tags.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "backend/bootstrap/connection"
5 | "errors"
6 | "github.com/jianfengye/collection"
7 | "github.com/jinzhu/gorm"
8 | "time"
9 | )
10 |
11 | const TaggableType_Topic = "topic"
12 |
13 | type Tag struct {
14 | CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
15 | ID int64 `gorm:"column:id;primary_key" json:"id;primary_key"`
16 | Name string `gorm:"column:name" json:"name"`
17 | Status string `gorm:"column:status" json:"status"`
18 | TopicCount int64 `gorm:"column:topic_count" json:"topic_count"`
19 | UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
20 | }
21 |
22 | // TableName sets the insert table name for this struct type
23 | func (t *Tag) TableName() string {
24 | return "tags"
25 | }
26 |
27 | func GetAllTags() ([]Tag, error) {
28 | var tags []Tag
29 | if err := connection.Default.Find(&tags).Error; err != nil {
30 | if gorm.IsRecordNotFoundError(err) {
31 | return nil, nil
32 | }
33 | return nil, err
34 | }
35 | return tags, nil
36 | }
37 |
38 | // 获取话题的标签
39 | func GetTopicTags(topicId int64) ([]Tag, error) {
40 | var taggables []Taggable
41 | if err := connection.Default.Model(&Taggable{}).Where("taggable_id = ?", topicId).Where("taggable_type = ?", TaggableType_Topic).Find(&taggables).Error; err != nil {
42 | if gorm.IsRecordNotFoundError(err) {
43 | return nil, nil
44 | }
45 | return nil, err
46 | }
47 |
48 | taggableColl := collection.NewObjCollection(taggables)
49 | tagIds, err := taggableColl.Pluck("TagID").ToInt64s()
50 | if err != nil {
51 | return nil, err
52 | }
53 |
54 | var tags []Tag
55 | if err := connection.Default.Model(&Tag{}).Where("id in (?)", tagIds).Find(&tags).Error; err != nil {
56 | if gorm.IsRecordNotFoundError(err) {
57 | return nil, nil
58 | }
59 | return nil, err
60 | }
61 | return tags, nil
62 | }
63 |
64 | // 更新一个话题的所有标签, 这里使用全删全加的方式
65 | func UpdateTopicTags(topicId int64, tagIds []int64) error {
66 | // 确保所有的标签都是存在的
67 | var tags []Tag
68 | if err := connection.Default.Model(&Tag{}).Where("id in (?)", tagIds).Find(&tags).Error; err != nil {
69 | return err
70 | }
71 |
72 | if len(tags) != len(tagIds) {
73 | return errors.New("标签传递错误")
74 | }
75 |
76 | var toAddTagIds []int64
77 |
78 | // 获取这个话题已经有的标签
79 | if err := connection.Default.Model(&Taggable{}).Where("taggable_id = ?", topicId).Where("taggable_type = ?", TaggableType_Topic).Delete(&Taggable{}).Error; err != nil {
80 | return err
81 | }
82 |
83 | for _, toAddTagId := range toAddTagIds {
84 | toAdd := Taggable{
85 | TagID: toAddTagId,
86 | TaggableID: topicId,
87 | TaggableType: TaggableType_Topic,
88 | }
89 | if err := connection.Default.Model(&Taggable{}).Save(toAdd).Error; err != nil {
90 | return err
91 | }
92 | }
93 |
94 | return nil
95 | }
96 |
97 | // 把多个标签放到一个话题
98 | func AddTagsToTopic(topicId int64, tagIds []int64) error {
99 | // 确保所有的标签都是存在的
100 | var tags []Tag
101 | if err := connection.Default.Model(&Tag{}).Where("id in (?)", tagIds).Find(&tags).Error; err != nil {
102 | return err
103 | }
104 |
105 | if len(tags) != len(tagIds) {
106 | return errors.New("标签传递错误")
107 | }
108 |
109 | var toAddTagIds []int64
110 |
111 | // 获取这个话题已经有的标签
112 | var taggables []Taggable
113 | if err := connection.Default.Model(&Taggable{}).Where("taggable_id = ?", topicId).Where("taggable_type = ?", TaggableType_Topic).Find(&taggables).Error; err != nil {
114 | if !gorm.IsRecordNotFoundError(err) {
115 | return err
116 | }
117 | }
118 |
119 | taggableColl := collection.NewObjCollection(taggables)
120 | tagIdColl := collection.NewInt64Collection(tagIds)
121 |
122 | toAddTagIds, err := tagIdColl.Diff(taggableColl.Pluck("TaggableID").Unique()).ToInt64s()
123 | if err != nil {
124 | return err
125 | }
126 |
127 | if len(toAddTagIds) == 0 {
128 | return nil
129 | }
130 |
131 | for _, toAddTagId := range toAddTagIds {
132 | // TODO 这里可以优化为批量插入
133 | toAdd := Taggable{
134 | TagID: toAddTagId,
135 | TaggableType: TaggableType_Topic,
136 | TaggableID: topicId,
137 | }
138 |
139 | if err := connection.Default.Model(&Taggable{}).Save(&toAdd).Error; err != nil {
140 | return err
141 | }
142 | }
143 |
144 | return nil
145 | }
--------------------------------------------------------------------------------
/model/topics.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "backend/bootstrap/connection"
5 | "github.com/jianfengye/collection"
6 | "github.com/jinzhu/gorm"
7 | "time"
8 | )
9 |
10 | const (
11 | TOPIC_STATUS_REVIEWED = "reviewed"
12 | TOPIC_STATUS_UNREVIEWED = "unreviewed"
13 | TOPIC_STATUS_DELETED = "deleted"
14 | )
15 |
16 | type Topic struct {
17 | Category string `gorm:"column:category" json:"category"`
18 | Content string `gorm:"column:content" json:"content"`
19 | CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
20 | ID int64 `gorm:"column:id;primary_key" json:"id;primary_key"`
21 | LikeCount int64 `gorm:"column:like_count" json:"like_count"`
22 | CommentCount int64 `gorm:"column:comment_count" json:"comment_count"`
23 | Link string `gorm:"column:link" json:"link"`
24 | Score int64 `gorm:"column:score" json:"score"`
25 | Source string `gorm:"column:source" json:"source"`
26 | Status string `gorm:"column:status" json:"status"`
27 | Title string `gorm:"column:title" json:"title"`
28 | UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
29 | UserID int64 `gorm:"column:user_id" json:"user_id"`
30 | User User `gorm:"ForeignKey:user_id"`
31 | }
32 |
33 | // TableName sets the insert table name for this struct type
34 | func (t *Topic) TableName() string {
35 | return "topics"
36 | }
37 |
38 | func GetTopicsByTagPager(tagId, offset int64, size int64) ([]Topic, int64, error) {
39 | var sum int64
40 | var topics []Topic
41 | var tag Tag
42 | if err := connection.Default.Model(&Tag{}).First(&tag, tagId).Error; err != nil {
43 | if gorm.IsRecordNotFoundError(err) {
44 | return nil, 0, nil
45 | }
46 | return nil, 0, err
47 | }
48 |
49 | var taggables []Taggable
50 | if err := connection.Default.Model(&Taggable{}).Count(&sum).Where("taggable_type = ?", TaggableType_Topic).Where("tag_id = ?", tagId).Order("updated_at desc").Offset(offset).Limit(size).Find(&taggables).Error; err != nil {
51 | if gorm.IsRecordNotFoundError(err) {
52 | return nil, 0, nil
53 | }
54 | return nil, 0, err
55 | }
56 |
57 | taggableColl := collection.NewObjCollection(taggables)
58 | topicIds, err := taggableColl.Pluck("TaggableID").ToInt64s()
59 | if err != nil {
60 | return nil, sum, err
61 | }
62 |
63 | if err := connection.Default.Model(&Topic{}).Where("id in (?)", topicIds).Where("status = ?", TOPIC_STATUS_REVIEWED).Find(&topics).Error; err != nil {
64 | if gorm.IsRecordNotFoundError(err) {
65 | return nil, sum, nil
66 | }
67 | return nil, sum, err
68 | }
69 | return topics, sum, nil
70 |
71 | }
72 |
73 | // 分页获取所有的topic列表
74 | func GetTopicsByPager(offset int64, size int64) ([]Topic, int64, error){
75 | var sum int64
76 | var topics []Topic
77 | err := connection.Default.Model(&Topic{}).Count(&sum).Where("status = ?", "reviewed").Offset(offset).Limit(size).Preload("User").Find(&topics).Error
78 | if err == nil {
79 | return topics, sum, nil
80 | }
81 |
82 | if gorm.IsRecordNotFoundError(err) {
83 | return nil, sum, nil
84 | }
85 |
86 | return nil, sum, nil
87 | }
88 |
89 |
90 | // 获取一个topic
91 | func GetTopicWithUser(id int64) *Topic {
92 | var topic Topic
93 | err := connection.Default.Model(&Topic{}).Where("status = ?", "reviewed").Preload("User").First(&topic, id).Error
94 | if err == nil {
95 | return &topic
96 | }
97 |
98 | return nil
99 | }
100 |
101 | // 获取一个topic
102 | func GetTopic(id int64) *Topic {
103 | var topic Topic
104 | err := connection.Default.Model(&Topic{}).Where("status = ?", "reviewed").First(&topic, id).Error
105 | if err == nil {
106 | return &topic
107 | }
108 |
109 | return nil
110 | }
111 |
112 | // 删除一个话题
113 | func DeleteTopic(id int64) error {
114 | if err := connection.Default.Model(&Topic{}).Set("status", TOPIC_STATUS_DELETED).Where("id = ?", id).Error; err != nil {
115 | return err
116 | }
117 | return nil
118 | }
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/model/users.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "time"
4 |
5 | type User struct {
6 | CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
7 | Email string `gorm:"column:email" json:"email"`
8 | ID int64 `gorm:"column:id;primary_key" json:"id;primary_key"`
9 | Name string `gorm:"column:name" json:"name"`
10 | Password string `gorm:"column:password" json:"password"`
11 | UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
12 | }
13 |
14 | // TableName sets the insert table name for this struct type
15 | func (u *User) TableName() string {
16 | return "users"
17 | }
--------------------------------------------------------------------------------
/storage/log/.gitkeeper:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huoding-fullstacks/backend/3955cf6dc76e75af40c985bff03677e5c94eeddd/storage/log/.gitkeeper
--------------------------------------------------------------------------------
/storage/log/hade.log:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huoding-fullstacks/backend/3955cf6dc76e75af40c985bff03677e5c94eeddd/storage/log/hade.log
--------------------------------------------------------------------------------
/swagger.yaml:
--------------------------------------------------------------------------------
1 | swagger: "2.0"
2 | info:
3 | description: "这是后端接口项目"
4 | version: "1.0.0"
5 | title: "后端"
6 | host: "localhost:8801"
7 | basePath: "/api"
8 | tags:
9 | - name: 话题
10 | description: 话题相关接口
11 | - name: 评论
12 | description: 评论相关接口
13 | - name: 标签
14 | description: 标签相关接口
15 | - name: 知识分享
16 | description: 知识点相关接口
17 | schemes:
18 | - http
19 | paths:
20 | /topic/list:
21 | get:
22 | summary: 获取话题列表
23 | description: 这个接口获取所有话题列表,分页显示
24 | tags:
25 | - 话题
26 | parameters:
27 | - name: page
28 | in: query
29 | description: 获取的页面数
30 | required: false
31 | type: integer
32 | format: int
33 | default: 0
34 | - name: size
35 | in: query
36 | description: 页面的展示条数,默认为20
37 | required: false
38 | type: integer
39 | format: int
40 | default: 20
41 | responses:
42 | 200:
43 | description: 成功操作
44 | schema:
45 | type: object
46 | properties:
47 | topics:
48 | type: object
49 | $ref: '#/definitions/topic_summarys'
50 | pager:
51 | type: object
52 | $ref: '#/definitions/pager'
53 | 500:
54 | description: 参数错误
55 | schema:
56 | $ref: '#/definitions/error_response'
57 | /topic/detail:
58 | get:
59 | summary: 获取单个话题详情
60 | description: 根据id获取单个话题详情
61 | tags:
62 | - 话题
63 | parameters:
64 | - name: topic_id
65 | in: query
66 | description: 话题主键
67 | required: true
68 | type: "integer"
69 | format: "int64"
70 | responses:
71 | 200:
72 | description: 成功返回
73 | schema:
74 | $ref: '#/definitions/topic'
75 | 500:
76 | description: 参数错误
77 | schema:
78 | $ref: '#/definitions/error_response'
79 | /topic/update:
80 | post:
81 | tags:
82 | - 话题
83 | summary: 更新某个话题
84 | consumes:
85 | - "multipart/form-data"
86 | produces:
87 | - "application/json"
88 | description: 更新话题
89 | parameters:
90 | - name: topic_id
91 | type: integer
92 | in: formData
93 | description: 话题id
94 | required: true
95 | - name: title
96 | type: string
97 | in: formData
98 | description: 话题的标题
99 | required: true
100 | - name: content
101 | type: string
102 | in: formData
103 | description: 话题内容
104 | required: true
105 | - name: link
106 | type: string
107 | description: 话题链接
108 | in: formData
109 | required: true
110 | - name: tag_ids
111 | type: array
112 | in: formData
113 | items:
114 | type: integer
115 | description: 话题标签
116 | required: true
117 | responses:
118 | 200:
119 | description: 返回成功
120 | 500:
121 | description: 参数错误
122 | schema:
123 | $ref: '#/definitions/error_response'
124 | /topic/create:
125 | post:
126 | consumes:
127 | - "multipart/form-data"
128 | produces:
129 | - "application/json"
130 | tags:
131 | - 话题
132 | description: 创建话题
133 | summary: 创建话题
134 | parameters:
135 | - name: title
136 | type: string
137 | in: formData
138 | description: 话题的标题
139 | - name: content
140 | type: string
141 | in: formData
142 | description: 话题内容
143 | - name: link
144 | type: string
145 | description: 话题链接
146 | in: formData
147 | - name: tag_ids
148 | type: array
149 | description: 话题标签
150 | in: formData
151 | items:
152 | type: integer
153 | responses:
154 | 200:
155 | description: 创建成功
156 | schema:
157 | $ref: '#/definitions/topic_summary'
158 | 500:
159 | description: 参数错误
160 | schema:
161 | $ref: '#/definitions/error_response'
162 | /topic/delete:
163 | post:
164 | tags:
165 | - 话题
166 | summary: 删除话题
167 | description: 删除话题
168 | consumes:
169 | - "multipart/form-data"
170 | produces:
171 | - "application/json"
172 | parameters:
173 | - name: topic_id
174 | in: formData
175 | required: true
176 | type: integer
177 | description: 话题id
178 | responses:
179 | 200:
180 | description: 删除成功
181 | 500:
182 | description: 参数错误
183 | schema:
184 | $ref: '#/definitions/error_response'
185 | /topic/like:
186 | post:
187 | tags:
188 | - 话题
189 | summary: 给话题点赞
190 | description: 给话题点赞
191 | consumes:
192 | - "multipart/form-data"
193 | produces:
194 | - "application/json"
195 | parameters:
196 | - name: topic_id
197 | type: integer
198 | in: formData
199 | description: 话题id
200 | required: true
201 | responses:
202 | 200:
203 | description: 操作成功
204 | /comment/create:
205 | post:
206 | tags:
207 | - 评论
208 | summary: 创建某个话题的评论
209 | description: 创建某个话题的评论
210 | consumes:
211 | - "multipart/form-data"
212 | produces:
213 | - "application/json"
214 | parameters:
215 | - name: topic_id
216 | type: integer
217 | in: formData
218 | description: 话题id
219 | required: true
220 | - name: content
221 | type: string
222 | in: formData
223 | description: 评论内容
224 | required: true
225 | responses:
226 | 200:
227 | description: 创建评论成功
228 | schema:
229 | $ref: '#/definitions/comment_summary'
230 | 500:
231 | description: 参数错误
232 | schema:
233 | $ref: '#/definitions/error_response'
234 | /comment/delete:
235 | post:
236 | tags:
237 | - 评论
238 | summary: 删除某个评论
239 | description: 只能删除自己的评论,是软删除,内容显示评论已删除
240 | consumes:
241 | - "multipart/form-data"
242 | parameters:
243 | - name: comment_id
244 | type: integer
245 | in: formData
246 | description: 评论id
247 | required: true
248 | responses:
249 | 200:
250 | description: 删除评论成功
251 | 500:
252 | description: 参数错误
253 | schema:
254 | $ref: '#/definitions/error_response'
255 | /comment/list:
256 | get:
257 | tags:
258 | - 评论
259 | summary: 获取某个话题的所有评论
260 | description: 获取某个话题的所有评论
261 | parameters:
262 | - name: topic_id
263 | type: integer
264 | in: query
265 | description: 话题id
266 | required: true
267 | responses:
268 | 200:
269 | description: 获取话题的所有评论
270 | schema:
271 | $ref: '#/definitions/comments'
272 | 500:
273 | description: 参数错误
274 | schema:
275 | $ref: '#/definitions/error_response'
276 | /comment/append:
277 | post:
278 | tags:
279 | - 评论
280 | summary: 增加二级评论
281 | description: 为某个一级评论增加二级评论
282 | consumes:
283 | - "multipart/form-data"
284 | parameters:
285 | - name: comment_id
286 | type: integer
287 | in: formData
288 | description: 评论id
289 | required: true
290 | - name: content
291 | type: string
292 | in: formData
293 | description: 二级评论内容
294 | required: true
295 | responses:
296 | 200:
297 | description: 增加二级评论成功
298 | schema:
299 | $ref: '#/definitions/comment_summary'
300 | 500:
301 | description: 参数错误
302 | schema:
303 | $ref: '#/definitions/error_response'
304 | /tag/list:
305 | get:
306 | tags:
307 | - 标签
308 | summary: 获取所有标签
309 | description: 获取所有标签列表
310 | consumes:
311 | - "multipart/form-data"
312 | responses:
313 | 200:
314 | description: 创建评论成功
315 | schema:
316 | $ref: '#/definitions/tags'
317 | 500:
318 | description: 参数错误
319 | schema:
320 | $ref: '#/definitions/error_response'
321 | /tag/topics:
322 | get:
323 | summary: 获取话题列表
324 | description: 这个接口获取所有话题列表,分页显示
325 | tags:
326 | - 标签
327 | parameters:
328 | - name: page
329 | in: query
330 | description: 获取的页面数
331 | required: false
332 | type: integer
333 | format: int
334 | default: 0
335 | - name: size
336 | in: query
337 | description: 页面的展示条数,默认为20
338 | required: false
339 | type: integer
340 | format: int
341 | default: 20
342 | - name: tag_id
343 | in: query
344 | description: 标签id
345 | required: true
346 | type: integer
347 | default: 1
348 | responses:
349 | 200:
350 | description: 成功操作
351 | schema:
352 | type: object
353 | properties:
354 | topics:
355 | type: object
356 | $ref: '#/definitions/topic_summarys'
357 | pager:
358 | type: object
359 | $ref: '#/definitions/pager'
360 | 500:
361 | description: 参数错误
362 | schema:
363 | $ref: '#/definitions/error_response'
364 | /knowledge/daily:
365 | get:
366 | summary: 获取每日知识点
367 | description: 获取某一天的每日知识点
368 | tags:
369 | - 知识分享
370 | parameters:
371 | - name: day
372 | in: query
373 | description: 获取知识点的日期
374 | required: true
375 | type: string
376 | default: "2019-09-01"
377 | responses:
378 | 200:
379 | description: 成功操作
380 | schema:
381 | $ref: "#/definitions/daily"
382 | 500:
383 | description: 参数错误
384 | schema:
385 | $ref: '#/definitions/error_response'
386 | definitions:
387 | user_summary:
388 | type: object
389 | description: 用于列表页等用户信息显示
390 | required:
391 | - id
392 | - name
393 | - small_image
394 | properties:
395 | id:
396 | type: integer
397 | format: int
398 | description: 用户唯一id
399 | name:
400 | type: string
401 | format: string
402 | description: 用户显示名称
403 | small_image:
404 | type: string
405 | format: string
406 | description: 用户显示小头像
407 | topic_summary:
408 | type: object
409 | description: 简要话题结构
410 | required:
411 | - id
412 | - title
413 | - content_summary
414 | - source
415 | - link
416 | - score
417 | - like_count
418 | - comment_count
419 | - created_at
420 | - user
421 | properties:
422 | id:
423 | type: integer
424 | format: int64
425 | description: 话题唯一标识别
426 | title:
427 | type: string
428 | description: 话题的标题
429 | content_summary:
430 | type: string
431 | description: 话题的内容概要,简略至100字以内
432 | source:
433 | type: string
434 | description: 话题来源
435 | enum:
436 | - 论坛
437 | - 自发
438 | link:
439 | type: string
440 | format: uri
441 | description: 话题外部链接,可能为空
442 | score:
443 | type: number
444 | format: float
445 | description: 话题评分
446 | like_count:
447 | type: integer
448 | format: int64
449 | description: 点赞数
450 | comment_count:
451 | type: integer
452 | format: int64
453 | description: 评论数
454 | created_at:
455 | type: string
456 | description: 创建时间
457 | user:
458 | $ref: '#/definitions/user_summary'
459 | topic:
460 | type: object
461 | description: 简要话题结构
462 | required:
463 | - id
464 | - title
465 | - content
466 | - source
467 | - link
468 | - score
469 | - like_count
470 | - comment_count
471 | - created_at
472 | - user
473 | - tags
474 | properties:
475 | id:
476 | type: integer
477 | format: int64
478 | description: 话题唯一标识别
479 | title:
480 | type: string
481 | description: 话题的标题
482 | content:
483 | type: string
484 | description: 话题的全部内容
485 | source:
486 | type: string
487 | description: 话题来源
488 | enum:
489 | - 论坛
490 | - 自发
491 | link:
492 | type: string
493 | description: 话题外部链接,可能为空
494 | score:
495 | type: number
496 | format: float
497 | description: 话题评分
498 | like_count:
499 | type: integer
500 | format: int64
501 | description: 点赞数
502 | comment_count:
503 | type: integer
504 | format: int64
505 | description: 评论数
506 | created_at:
507 | type: string
508 | description: 创建时间
509 | user:
510 | type: object
511 | $ref: '#/definitions/user_summary'
512 | tags:
513 | type: array
514 | items:
515 | $ref: '#/definitions/tag'
516 | topic_summarys:
517 | type: array
518 | description: 列表页返回的数据格式
519 | items:
520 | $ref: '#/definitions/topic_summary'
521 | pager:
522 | type: object
523 | description: 分页信息
524 | required:
525 | - size
526 | - page
527 | - total_page
528 | properties:
529 | size:
530 | type: integer
531 | description: 单页个数
532 | example: 20
533 | page:
534 | type: integer
535 | description: 当前页面
536 | example: 1
537 | total_page:
538 | type: integer
539 | description: 总共页面数
540 | example: 4
541 | error_response:
542 | type: object
543 | description: 错误返回信息
544 | required:
545 | - code
546 | - msg
547 | properties:
548 | code:
549 | type: integer
550 | description: 自定义错误码
551 | example: 50001
552 | msg:
553 | type: string
554 | description: 错误信息
555 | example: 话题不存在
556 | comment_summary:
557 | type: object
558 | description: 简要评论结构
559 | required:
560 | - id
561 | - content
562 | - like_count
563 | - parent_id
564 | - created_at
565 | - user
566 | properties:
567 | id:
568 | type: integer
569 | format: int64
570 | description: 评论唯一标示
571 | content:
572 | type: string
573 | description: 评论全部内容
574 | like_count:
575 | type: integer
576 | format: int64
577 | description: 点赞数
578 | parent_id:
579 | type: integer
580 | format: int64
581 | description: 父级别评论的id
582 | created_at:
583 | type: string
584 | description: 创建时间
585 | user:
586 | type: object
587 | $ref: '#/definitions/user_summary'
588 | comment_detail:
589 | type: object
590 | description: 简要评论结构
591 | required:
592 | - id
593 | - content
594 | - like_count
595 | - parent_id
596 | - created_at
597 | - user
598 | - comments
599 | properties:
600 | id:
601 | type: integer
602 | format: int64
603 | description: 评论唯一标示
604 | content:
605 | type: string
606 | description: 评论全部内容
607 | like_count:
608 | type: integer
609 | format: int64
610 | description: 点赞数
611 | parent_id:
612 | type: integer
613 | format: int64
614 | description: 父级别评论的id
615 | created_at:
616 | type: string
617 | description: 创建时间
618 | user:
619 | type: object
620 | $ref: '#/definitions/user_summary'
621 | comments:
622 | type: array
623 | description: 子级别评论,最多只有两层,不会有更多层
624 | items:
625 | $ref: '#/definitions/comment_summary'
626 | comments:
627 | type: array
628 | description: 评论详情列表
629 | items:
630 | $ref: '#/definitions/comment_detail'
631 | tag:
632 | type: object
633 | description: 标签
634 | required:
635 | - id
636 | - name
637 | - topic_count
638 | properties:
639 | id:
640 | type: integer
641 | format: int64
642 | description: 标签唯一标示
643 | example: 1
644 | name:
645 | type: string
646 | description: 标签名称
647 | example: golang
648 | topic_count:
649 | type: integer
650 | description: 话题数
651 | example: 10
652 | tags:
653 | type: array
654 | description: 标签列表
655 | items:
656 | $ref: '#/definitions/tag'
657 | daily:
658 | type: object
659 | description: 本日知识点结构
660 | required:
661 | - id
662 | - title
663 | - author
664 | - day
665 | - content
666 | properties:
667 | id:
668 | type: integer
669 | format: int64
670 | description: 每日唯一标示
671 | example: 1
672 | title:
673 | type: string
674 | description: 知识点标题
675 | example: go中多goroutine有几种方法进行数据同步?
676 | author:
677 | type: string
678 | description: 作者
679 | example: 轩脉刃
680 | day:
681 | type: string
682 | description: 日期
683 | example: 2019-01-02
684 | created_at:
685 | type: string
686 | description: 创建时间
687 | content:
688 | type: array
689 | description: 具体今日知识点的内容
690 | items:
691 | type: object
692 | properties:
693 | title:
694 | type: string
695 | example: 深入go实现
696 | link:
697 | type: string
698 | example: http://baidu.com
699 | comment:
700 | type: string
701 | example: 评论内容
--------------------------------------------------------------------------------
/tester/coverage.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | package tester
3 |
--------------------------------------------------------------------------------
/tester/env/.gitkeeper:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huoding-fullstacks/backend/3955cf6dc76e75af40c985bff03677e5c94eeddd/tester/env/.gitkeeper
--------------------------------------------------------------------------------
/tester/readme.md:
--------------------------------------------------------------------------------
1 | tester存放的是集成测试的内容
2 |
3 | 目录
4 |
5 | # suites
6 |
7 | 存放测试套件,每个测试套件
8 |
9 | ## suites/xxx
10 |
11 | 这里存放测试套件,测试套件文件夹需要包含before.go和after.go两个文件
12 |
13 | before.go存放有
14 |
15 | * SetUp() 函数,这个函数在Suite运行之前会运行
16 | * Before() 函数,这个函数在所有Case运行之前运行
17 |
18 | after.go存放有
19 |
20 | * TearDown() 函数,这个函数在Suite运行之后会运行
21 | * After() 函数,这个函数在Suite运行之后运行
22 |
23 | # envionment
24 |
25 | 初始化测试环境的工具
26 |
27 | 这个工具里面只做
28 |
29 | * 表的搭建
30 |
31 | # report
32 |
33 | 存放报告的地址
34 |
35 | 代码覆盖率需要额外跑脚本
36 |
37 | 在tester目录下运行:
38 |
39 | `sh coverage.sh` 会在report下生成coverage.out和coverage.html,并自动打开浏览器
--------------------------------------------------------------------------------
/tester/report/.gitkeeper:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huoding-fullstacks/backend/3955cf6dc76e75af40c985bff03677e5c94eeddd/tester/report/.gitkeeper
--------------------------------------------------------------------------------
/tester/suite/suite1/after.go:
--------------------------------------------------------------------------------
1 | package suite1
2 |
3 | func TearDown() {
4 |
5 | }
6 |
7 | func After() {
8 |
9 | }
--------------------------------------------------------------------------------
/tester/suite/suite1/before.go:
--------------------------------------------------------------------------------
1 | package suite1
2 |
3 | import (
4 | "backend/bootstrap/config"
5 | "backend/bootstrap/connection"
6 | "backend/bootstrap/logger"
7 | "backend/model"
8 | "backend/util"
9 | "log"
10 | "path"
11 | )
12 |
13 | func SetUp() {
14 | cf := path.Join(util.RootFolder(), "env.test.yaml")
15 | if err := config.Init(cf); err != nil {
16 | log.Fatalln(err)
17 | }
18 |
19 | // 加载日志
20 | if err := logger.Init(config.Default); err != nil {
21 | log.Fatalln(err)
22 | }
23 |
24 | // 加载数据库
25 | if err := connection.Init(config.Default); err != nil {
26 | log.Fatalln(err)
27 | }
28 | }
29 |
30 |
31 | func Before() {
32 | // 清空所有数据
33 | connection.Default.Delete(&model.User{})
34 | connection.Default.Delete(&model.Topic{})
35 | connection.Default.Delete(&model.Tag{})
36 | connection.Default.Delete(&model.Comment{})
37 | connection.Default.Delete(&model.Likeable{})
38 | connection.Default.Delete(&model.Taggable{})
39 | initDB()
40 | }
41 |
--------------------------------------------------------------------------------
/tester/suite/suite1/case_normal_1.go:
--------------------------------------------------------------------------------
1 | package suite1
2 | /*
3 | import (
4 | "backend/bootstrap/connection"
5 | "backend/controller/comment"
6 | "backend/controller/tag"
7 | "backend/controller/topic"
8 | "backend/model"
9 | "backend/view/swagger/models"
10 | "encoding/json"
11 | "fmt"
12 | "github.com/gin-gonic/gin"
13 | . "github.com/smartystreets/goconvey/convey"
14 | "io/ioutil"
15 | "net/http"
16 | "net/http/httptest"
17 | "net/url"
18 | "strings"
19 | "testing"
20 | )
21 |
22 | func NormalCase1(t *testing.T) {
23 | Convey("测试正常流程", t, func() {
24 | router := gin.New()
25 | api := router.Group("/api")
26 | topic.Register(api)
27 | tag.Register(api)
28 | comment.Register(api)
29 |
30 | Convey("查找所有标签", func() {
31 | w := httptest.NewRecorder()
32 | r := httptest.NewRequest("GET", "/api/tag/list", nil)
33 | router.ServeHTTP(w, r)
34 | resp := w.Result()
35 | res, _ := ioutil.ReadAll(resp.Body)
36 |
37 | sRes := []*models.Tag{}
38 |
39 | err := json.Unmarshal(res, &sRes)
40 | So(err, ShouldBeNil)
41 |
42 | Convey("检查标签个数为3", func() {
43 | So(len(sRes), ShouldEqual, 3)
44 | })
45 |
46 | })
47 |
48 | Convey("创建一个话题,带有1个标签", func() {
49 | params := url.Values{
50 | "title": []string{"php是世界上最好的语言"},
51 | "content": []string{"php是世界上最好的语言
"},
52 | "link": []string{"http://baidu.com"},
53 | "tag_ids": []string{"1"},
54 | }
55 | body := strings.NewReader(params.Encode())
56 | w := httptest.NewRecorder()
57 |
58 | r1 := httptest.NewRequest("POST", "/api/topic/create", body)
59 | r1.Header.Set("Content-Type", "application/x-www-form-urlencoded")
60 | router.ServeHTTP(w, r1)
61 | resp := w.Result()
62 | So(resp.StatusCode, ShouldEqual, http.StatusOK)
63 |
64 | var sum int
65 | connection.Default.Model(&model.Topic{}).Count(&sum)
66 |
67 | var topic1 model.Topic
68 | connection.Default.Where("title= ?", "php是世界上最好的语言").First(&topic1)
69 |
70 | Convey("查找这个话题", func() {
71 | w := httptest.NewRecorder()
72 | r := httptest.NewRequest("GET", "/api/topic/detail?topic_id=" + fmt.Sprint(topic1.ID), nil)
73 | router.ServeHTTP(w, r)
74 | resp := w.Result()
75 | res, err := ioutil.ReadAll(resp.Body)
76 | So(resp.StatusCode,ShouldEqual, http.StatusOK)
77 |
78 | So(err, ShouldBeNil)
79 | sRes := models.Topic{}
80 | err = json.Unmarshal(res, &sRes)
81 | So(err, ShouldBeNil)
82 |
83 | Convey("检查话题内容", func() {
84 | So(sRes.Content, ShouldEqual, topic1.Content)
85 | })
86 |
87 | Convey("检查话题标签", func() {
88 | So(len(sRes.Tags),ShouldEqual, 1)
89 | })
90 |
91 | })
92 |
93 | Convey("更新话题的内容", func() {
94 |
95 | params := url.Values{
96 | "title": []string{"php是世界上最好的语言??"},
97 | "content": []string{"php是世界上最好的语言
"},
98 | "link": []string{""},
99 | "tag_ids": []string{"2"},
100 | "topic_id": []string{fmt.Sprint(topic1.ID)},
101 | }
102 | body := strings.NewReader(params.Encode())
103 | w := httptest.NewRecorder()
104 |
105 | r := httptest.NewRequest("POST", "/api/topic/update", body)
106 | r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
107 | router.ServeHTTP(w, r)
108 | resp := w.Result()
109 | res, err := ioutil.ReadAll(resp.Body)
110 | So(err, ShouldBeNil)
111 | t.Log(string(res))
112 | So(resp.StatusCode, ShouldEqual, http.StatusOK)
113 |
114 | Convey("查找这个话题", func() {
115 |
116 | w := httptest.NewRecorder()
117 | r := httptest.NewRequest("GET", "/api/topic/detail?topic_id=" + fmt.Sprint(topic1.ID), nil)
118 | router.ServeHTTP(w, r)
119 | resp := w.Result()
120 | res, err := ioutil.ReadAll(resp.Body)
121 | So(resp.StatusCode,ShouldEqual, http.StatusOK)
122 |
123 | So(err, ShouldBeNil)
124 | sRes := models.Topic{}
125 | err = json.Unmarshal(res, &sRes)
126 | So(err, ShouldBeNil)
127 |
128 | Convey("检查话题内容", func() {
129 | So(sRes.Content, ShouldEqual, "php是世界上最好的语言
")
130 | So(sRes.Title, ShouldEqual, "php是世界上最好的语言??")
131 | })
132 | })
133 |
134 | })
135 |
136 | Convey("创建话题的评论", func() {
137 |
138 | params := url.Values{
139 | "content": []string{"严重同意
"},
140 | "topic_id": []string{fmt.Sprint(topic1.ID)},
141 | }
142 | body := strings.NewReader(params.Encode())
143 | w := httptest.NewRecorder()
144 |
145 | r := httptest.NewRequest("POST", "/api/comment/create", body)
146 | r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
147 | router.ServeHTTP(w, r)
148 | resp := w.Result()
149 | So(resp.StatusCode, ShouldEqual, http.StatusOK)
150 | res, err := ioutil.ReadAll(resp.Body)
151 | So(err, ShouldBeNil)
152 | t.Log(string(res))
153 |
154 | Convey("查找话题的所有评论", func() {
155 | w := httptest.NewRecorder()
156 | r := httptest.NewRequest("GET", "/api/comment/list?topic_id=" + fmt.Sprint(topic1.ID), nil)
157 | router.ServeHTTP(w, r)
158 | resp := w.Result()
159 | res, err := ioutil.ReadAll(resp.Body)
160 | t.Log(string(res))
161 | So(resp.StatusCode,ShouldEqual, http.StatusOK)
162 |
163 | Convey("检查话题的所有评论格式", func() {
164 | sRes := models.Comments{}
165 | err = json.Unmarshal(res, &sRes)
166 | So(err, ShouldBeNil)
167 | })
168 |
169 | })
170 |
171 | var comment1 model.Comment
172 | connection.Default.Model(&model.Comment{}).Where("topic_id =? ", topic1.ID).First(&comment1)
173 |
174 | Convey("创建评论的跟评", func() {
175 |
176 | params := url.Values{
177 | "content": []string{"php是世界上最好的语言??"},
178 | "comment_id" : []string{fmt.Sprint(comment1.ID)},
179 | }
180 | body := strings.NewReader(params.Encode())
181 | w := httptest.NewRecorder()
182 |
183 | r := httptest.NewRequest("POST", "/api/comment/append", body)
184 | r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
185 | router.ServeHTTP(w, r)
186 | resp := w.Result()
187 | res, err := ioutil.ReadAll(resp.Body)
188 | So(err, ShouldBeNil)
189 | t.Log(string(res))
190 | So(resp.StatusCode, ShouldEqual, http.StatusOK)
191 |
192 | Convey("查找话题的所有评论", func() {
193 | w := httptest.NewRecorder()
194 | r := httptest.NewRequest("GET", "/api/comment/list?topic_id=" + fmt.Sprint(topic1.ID), nil)
195 | router.ServeHTTP(w, r)
196 | resp := w.Result()
197 | res, err := ioutil.ReadAll(resp.Body)
198 | t.Log(string(res))
199 | So(resp.StatusCode,ShouldEqual, http.StatusOK)
200 |
201 | Convey("检查话题的所有评论格式", func() {
202 | sRes := models.Comments{}
203 | err = json.Unmarshal(res, &sRes)
204 | So(err, ShouldBeNil)
205 | So(len(sRes[0].Comments), ShouldEqual, 1)
206 | })
207 |
208 | })
209 | })
210 |
211 | })
212 | })
213 | })
214 |
215 | }
216 |
217 | */
--------------------------------------------------------------------------------
/tester/suite/suite1/case_topic.go:
--------------------------------------------------------------------------------
1 | package suite1
2 |
3 | /*
4 | func CaseTopic(t *testing.T) {
5 | Convey("测试topic相关流程", t, func() {
6 | router := gin.New()
7 | api := router.Group("/api")
8 | topic.Register(api)
9 | tag.Register(api)
10 | comment.Register(api)
11 |
12 | // 创建20个话题
13 | for i := 1; i <= 20; i++ {
14 | topic := model.Topic{
15 | Category: "",
16 | Content: "tester" + fmt.Sprint(i),
17 | CreatedAt: time.Now(),
18 | LikeCount: rand.Int63() + 1,
19 | Link: "",
20 | Score: 0,
21 | Source: "",
22 | Status: model.TOPIC_STATUS_REVIEWED,
23 | Title: "tester title " + fmt.Sprint(i),
24 | UpdatedAt: time.Now(),
25 | UserID: 1,
26 | }
27 | connection.Default.Save(&topic)
28 | }
29 |
30 | Convey("测试获取分页数据", func() {
31 | w := httptest.NewRecorder()
32 | r := httptest.NewRequest("GET", "/api/topic/list?page=1&size=10", nil)
33 | router.ServeHTTP(w, r)
34 | resp := w.Result()
35 | res, err := ioutil.ReadAll(resp.Body)
36 | So(resp.StatusCode,ShouldEqual, http.StatusOK)
37 |
38 | Convey("检查话题的格式", func() {
39 | sRes := models.GetTopicListOKBody{}
40 | err = json.Unmarshal(res, &sRes)
41 | So(err, ShouldBeNil)
42 |
43 | So(len(sRes.Topics), ShouldEqual, 0)
44 | So(sRes.Pager.TotalPage, ShouldEqual, 2)
45 | So(sRes.Topics[0].LikeCount, ShouldBeGreaterThan, 0)
46 | })
47 | })
48 | })
49 |
50 | }
51 |
52 | */
--------------------------------------------------------------------------------
/tester/suite/suite1/global.go:
--------------------------------------------------------------------------------
1 | package suite1
2 |
3 | import (
4 | "backend/bootstrap/connection"
5 | "backend/model"
6 | )
7 |
8 | var (
9 | user1 model.User
10 | user2 model.User
11 |
12 | tag1 model.Tag
13 | tag2 model.Tag
14 | tag3 model.Tag
15 |
16 | )
17 |
18 | func initDB() {
19 | user1 = model.User{
20 | Email: "tester1@huoding.com",
21 | ID: 1,
22 | Name: "tester1",
23 | Password: "",
24 | }
25 | user2 = model.User{
26 | Email: "tester2@huoding.com",
27 | ID: 2,
28 | Name: "tester2",
29 | Password: "",
30 | }
31 | tag1 = model.Tag{
32 | ID: 1,
33 | Name: "Golang",
34 | Status: "reviewed",
35 | TopicCount: 0,
36 | }
37 | tag2 = model.Tag{
38 | ID: 2,
39 | Name: "PHP",
40 | Status: "reviewed",
41 | TopicCount: 0,
42 | }
43 | tag3 = model.Tag{
44 | ID: 3,
45 | Name: "JavaScript",
46 | Status: "reviewed",
47 | TopicCount: 0,
48 | }
49 | connection.Default.Create(user1)
50 | connection.Default.Create(user2)
51 | connection.Default.Create(tag1)
52 | connection.Default.Create(tag2)
53 | connection.Default.Create(tag3)
54 | }
55 |
56 |
--------------------------------------------------------------------------------
/tester/suite/suite1/run_test.go:
--------------------------------------------------------------------------------
1 | package suite1
2 |
3 | import "testing"
4 | import . "github.com/smartystreets/goconvey/convey"
5 |
6 | func TestRunSuite(t *testing.T) {
7 | SetUp()
8 | defer TearDown()
9 | Convey("初始化", t, nil)
10 |
11 | //runCase(t, NormalCase1)
12 | //runCase(t, CaseTopic)
13 |
14 | }
15 |
16 | func runCase(t *testing.T, testCase func(*testing.T)) {
17 | Before()
18 | defer After()
19 |
20 | testCase(t)
21 | }
--------------------------------------------------------------------------------
/tester/suite/suite2/after.go:
--------------------------------------------------------------------------------
1 | package suite2
2 |
3 | func TearDown() {
4 |
5 | }
6 |
7 | func After() {
8 |
9 | }
--------------------------------------------------------------------------------
/tester/suite/suite2/before.go:
--------------------------------------------------------------------------------
1 | package suite2
2 |
3 | import (
4 | "backend/bootstrap/config"
5 | "backend/bootstrap/connection"
6 | "backend/bootstrap/logger"
7 | "backend/model"
8 | "backend/util"
9 | "log"
10 | "path"
11 | )
12 |
13 | func SetUp() {
14 | cf := path.Join(util.RootFolder(), "env.test.yaml")
15 | if err := config.Init(cf); err != nil {
16 | log.Fatalln(err)
17 | }
18 |
19 | // 加载日志
20 | if err := logger.Init(config.Default); err != nil {
21 | log.Fatalln(err)
22 | }
23 |
24 | // 加载数据库
25 | if err := connection.Init(config.Default); err != nil {
26 | log.Fatalln(err)
27 | }
28 | }
29 |
30 |
31 | func Before() {
32 | // 清空所有数据
33 | connection.Default.Delete(&model.Daily{})
34 | initDB()
35 | }
36 |
--------------------------------------------------------------------------------
/tester/suite/suite2/case_normal_1.go:
--------------------------------------------------------------------------------
1 | package suite2
2 |
3 | import (
4 | "backend/bootstrap/connection"
5 | "backend/controller/knowledge"
6 | "backend/model"
7 | "backend/view/swagger/models"
8 | "encoding/json"
9 | "github.com/gin-gonic/gin"
10 | . "github.com/smartystreets/goconvey/convey"
11 | "io/ioutil"
12 | "net/http/httptest"
13 | "testing"
14 | "time"
15 | )
16 |
17 | func NormalCase1(t *testing.T) {
18 | daily1 := model.Daily{
19 | CreatedAt: time.Time{},
20 | ID: 1,
21 | Title: "这是标题",
22 | Author: "轩脉刃",
23 | Day: "2019-9-1",
24 | Content: `[{"title": "这是标题", "link": "http://baidu.com", "comment": "这是评论"}]`,
25 | UpdatedAt: time.Time{},
26 | }
27 | connection.Default.Create(daily1)
28 | Convey("测试正常流程", t, func() {
29 | router := gin.New()
30 | api := router.Group("/api")
31 | knowledge.Register(api)
32 |
33 | Convey("查找当前日期", func() {
34 | w := httptest.NewRecorder()
35 | r := httptest.NewRequest("GET", "/api/knowledge/daily?day=2019-9-1", nil)
36 | router.ServeHTTP(w, r)
37 | resp := w.Result()
38 | res, _ := ioutil.ReadAll(resp.Body)
39 |
40 | sRes := models.Daily{}
41 |
42 | err := json.Unmarshal(res, &sRes)
43 | So(err, ShouldBeNil)
44 |
45 | Convey("检查返回值是否正确", func() {
46 | So(1, ShouldEqual, len(sRes.Content))
47 | So("这是标题", ShouldEqual, *sRes.Title)
48 | })
49 | })
50 |
51 | Convey("查找不存在日期", func() {
52 | w := httptest.NewRecorder()
53 | r := httptest.NewRequest("GET", "/api/knowledge/daily?day=2019-9-2", nil)
54 | router.ServeHTTP(w, r)
55 | resp := w.Result()
56 | res, _ := ioutil.ReadAll(resp.Body)
57 |
58 | sRes := models.ErrorResponse{}
59 |
60 | err := json.Unmarshal(res, &sRes)
61 | So(err, ShouldBeNil)
62 |
63 | Convey("检查返回值是否正确", func() {
64 | So("没有当日的信息", ShouldEqual, *sRes.Msg)
65 | })
66 | })
67 |
68 | })
69 |
70 | }
--------------------------------------------------------------------------------
/tester/suite/suite2/global.go:
--------------------------------------------------------------------------------
1 | package suite2
2 |
3 | var (
4 | )
5 |
6 | func initDB() {
7 |
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/tester/suite/suite2/run_test.go:
--------------------------------------------------------------------------------
1 | package suite2
2 |
3 | import "testing"
4 | import . "github.com/smartystreets/goconvey/convey"
5 |
6 | func TestRunSuite(t *testing.T) {
7 | SetUp()
8 | defer TearDown()
9 | Convey("初始化", t, nil)
10 |
11 | runCase(t, NormalCase1)
12 |
13 | }
14 |
15 | func runCase(t *testing.T, testCase func(*testing.T)) {
16 | Before()
17 | defer After()
18 |
19 | testCase(t)
20 | }
--------------------------------------------------------------------------------
/util/debug.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import "encoding/json"
4 |
5 | // PanicJson 快速打印出一个变量,并直接退出,加速调试
6 | func PanicJson(a interface{}) {
7 | bs, err := json.MarshalIndent(a, "", "\t")
8 | if err != nil {
9 | panic(err)
10 | }
11 | panic(string(bs))
12 | }
13 |
--------------------------------------------------------------------------------
/util/file.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "os"
5 | )
6 |
7 | // FileIsExist 确认文件是否存在
8 | func FileIsExist(file string) bool {
9 | if _, err := os.Stat(file); os.IsExist(err) || err == nil {
10 | return true
11 | }
12 | return false
13 | }
--------------------------------------------------------------------------------
/util/file_test.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import "testing"
4 |
5 | func Test_FileIsExist(t *testing.T) {
6 | file := "/Users/yejianfeng/Documents/workspace/huoding/backend/env.default.yaml"
7 | if FileIsExist(file) != true {
8 | t.Error("check file is exist error")
9 | }
10 | }
--------------------------------------------------------------------------------
/util/folder.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "path"
5 | "runtime"
6 | )
7 |
8 | func getCurrentFolder() string {
9 | _, filename, _, _ := runtime.Caller(1)
10 | return path.Dir(filename)
11 | }
12 |
13 | // RootFolder 获取根目录
14 | func RootFolder() string {
15 | return path.Dir(getCurrentFolder())
16 | }
17 |
18 | // StorageFolder 获取Storage文件夹
19 | func StorageFolder() string {
20 | return path.Join(RootFolder(), "storage")
21 | }
22 |
23 | // LogFolder 获取Storage/log文件夹
24 | func LogFolder() string {
25 | return path.Join(StorageFolder(), "log")
26 | }
27 |
28 | // TesterFolder 获取Tester文件夹
29 | func TesterFolder() string {
30 | return path.Join(RootFolder(), "tester")
31 | }
--------------------------------------------------------------------------------
/util/folder_test.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import(
4 | "testing"
5 | )
6 |
7 | func Test_Folder(t *testing.T) {
8 | folder := getCurrentFolder()
9 |
10 | t.Log(folder)
11 |
12 | t.Log(RootFolder())
13 | t.Log(StorageFolder())
14 | t.Log(LogFolder())
15 | t.Log(TesterFolder())
16 |
17 | }
--------------------------------------------------------------------------------
/util/response.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | )
7 |
8 | type ErrorResponse struct {
9 |
10 | // 自定义错误码
11 | Code int64 `json:"code,omitempty"`
12 |
13 | // 错误信息
14 | Msg string `json:"msg,omitempty"`
15 | }
16 |
17 | // 返回未知错误
18 | func AbortUnknownError(c *gin.Context, err error) {
19 | msg := "内部错误"
20 | if mode, existed := c.Get("error.mode"); existed && mode == "show" {
21 | msg = err.Error()
22 | }
23 | c.AbortWithStatusJSON(http.StatusInternalServerError, ErrorResponse{
24 | Code: http.StatusInternalServerError,
25 | Msg: msg,
26 | })
27 | }
28 |
29 | // 返回已知错误,这里的err直接显示,请确保对外显示不会泄漏
30 | func AbortError(c *gin.Context, code int64, err error) {
31 | c.AbortWithStatusJSON(http.StatusInternalServerError, ErrorResponse{
32 | Code: code,
33 | Msg: err.Error(),
34 | })
35 | }
36 |
37 | func ResponseSuccess(c *gin.Context, obj interface{}) {
38 | c.JSON(200, obj)
39 | }
--------------------------------------------------------------------------------
/util/time.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | var (
4 | TIME_ISO_NO_T = "2006-01-02 15:04:05"
5 | )
6 |
--------------------------------------------------------------------------------
/util/trace.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "bytes"
5 | "encoding/hex"
6 | "fmt"
7 | "github.com/gin-gonic/gin"
8 | "math/rand"
9 | "net"
10 | "time"
11 | )
12 |
13 | var TraceKey = "hade-trace-id"
14 |
15 | // NewTraceId 生成新的traceId
16 | func NewTraceId() string {
17 | // traceId 生成方法: 时间戳 + 机器ip + 随机数
18 |
19 | b := bytes.Buffer{}
20 |
21 | now := time.Now()
22 | timestamp := uint32(now.Unix())
23 | timeNano := now.UnixNano()
24 | b.WriteString(fmt.Sprintf("%08x", timestamp&0xffffffff))
25 | b.WriteString(fmt.Sprintf("%04x", timeNano&0xffff))
26 |
27 | interfaceAddr, err := net.InterfaceAddrs()
28 | if err != nil {
29 | return ""
30 | }
31 | localIp := net.ParseIP("127.0.0.1")
32 | for _, address := range interfaceAddr {
33 | ipNet, isValidIpNet := address.(*net.IPNet)
34 | if isValidIpNet && !ipNet.IP.IsLoopback() {
35 | if ipNet.IP.To4() != nil {
36 | localIp = ipNet.IP
37 | break
38 | }
39 | }
40 | }
41 |
42 | netIP := net.ParseIP(localIp.String())
43 | if netIP == nil {
44 | b.WriteString("00000000")
45 | } else {
46 | b.WriteString(hex.EncodeToString(netIP.To4()))
47 | }
48 |
49 | b.WriteString(fmt.Sprintf("%06x", rand.Int31n(1<<24)))
50 |
51 | return b.String()
52 | }
53 |
54 | //GetTraceId
55 | func GetTraceId(c *gin.Context) string {
56 | val, exists := c.Get(TraceKey)
57 | if exists {
58 | return val.(string)
59 | }
60 |
61 | if traceId := getTraceIdFromHttpHeader(c); traceId != "" {
62 | c.Set(TraceKey, traceId)
63 | return traceId
64 | }
65 |
66 | if traceId := NewTraceId(); traceId != "" {
67 | c.Set(TraceKey, traceId)
68 | return traceId
69 | }
70 |
71 | return ""
72 | }
73 |
74 | func getTraceIdFromHttpHeader(c *gin.Context) string {
75 | if traceId := c.Request.Header.Get(TraceKey); traceId != "" {
76 | return traceId
77 | }
78 | return ""
79 | }
80 |
81 | //SetTraceId
82 | func SetTraceId(c *gin.Context, traceId string) {
83 | c.Set(TraceKey, traceId)
84 | }
--------------------------------------------------------------------------------
/view/adapter/comment.go:
--------------------------------------------------------------------------------
1 | package adapter
2 |
3 | import (
4 | "backend/model"
5 | "backend/util"
6 | "backend/view/swagger/models"
7 | "github.com/jianfengye/collection"
8 | )
9 |
10 | // 转换成commentSummary结构
11 | func ToCommentSummary(comment model.Comment, user *model.User) *models.CommentSummary {
12 | t := comment.CreatedAt.Format(util.TIME_ISO_NO_T)
13 | out := &models.CommentSummary{
14 | Content: &comment.Content,
15 | CreatedAt: &t,
16 | ID: &comment.ID,
17 | LikeCount: &comment.LikeCount,
18 | ParentID: &comment.ParentID,
19 | }
20 | if user == nil {
21 | out.User = nil
22 | } else {
23 | out.User = ToUserSummary(*user)
24 | }
25 |
26 | return out
27 | }
28 |
29 |
30 | func ToComments(commentsWithUser []model.Comment) (*models.Comments, error) {
31 | commentColl := collection.NewObjCollection(commentsWithUser)
32 | level1Coll := commentColl.Filter(func(obj interface{}, index int) bool {
33 | comment := obj.(model.Comment)
34 | if comment.ParentID == 0 {
35 | return true
36 | }
37 | return false
38 | })
39 |
40 | out := make([]*models.CommentDetail, level1Coll.Count())
41 | for i := 0; i < level1Coll.Count(); i++ {
42 | item, err := level1Coll.Index(i).ToInterface()
43 | if err != nil {
44 | return nil, err
45 | }
46 | comment := item.(model.Comment)
47 | ut := ToUserSummary(comment.User)
48 | t1 := comment.CreatedAt.Format(util.TIME_ISO_NO_T)
49 | out[i] = &models.CommentDetail{
50 | Comments: nil,
51 | Content: &comment.Content,
52 | CreatedAt: &t1,
53 | ID: &comment.ID,
54 | LikeCount: &comment.LikeCount,
55 | ParentID: &comment.ParentID,
56 | User: ut,
57 | }
58 |
59 | // 补充二级评论
60 | level2Coll := commentColl.Filter(func(obj interface{}, index int) bool {
61 | t := obj.(model.Comment)
62 | if t.ParentID == comment.ID {
63 | return true
64 | }
65 | return false
66 | })
67 | if level2Coll.Count() == 0 {
68 | continue
69 | }
70 | level2Coll.SetCompare(func(a interface{}, b interface{}) int {
71 | aModel := a.(model.Comment)
72 | bModel := b.(model.Comment)
73 | aTime := aModel.CreatedAt
74 | bTime := bModel.CreatedAt
75 | if aTime.Before(bTime) {
76 | return 1
77 | }
78 | if aTime.Equal(bTime) {
79 | return 0
80 | }
81 | return -1
82 | }).SortDesc()
83 |
84 | out[i].Comments = make([]*models.CommentSummary, level2Coll.Count())
85 | for j := 0; j < level2Coll.Count(); j++{
86 | t, err := level2Coll.Index(j).ToInterface()
87 | if err != nil {
88 | return nil, err
89 | }
90 | ts := t.(model.Comment)
91 | out[i].Comments[j] = ToCommentSummary(ts, &ts.User)
92 | }
93 | }
94 | o := models.Comments(out)
95 | return &o, nil
96 | }
--------------------------------------------------------------------------------
/view/adapter/comment_test.go:
--------------------------------------------------------------------------------
1 | package adapter
2 |
3 | import (
4 | "backend/model"
5 | "backend/view/swagger/models"
6 | "reflect"
7 | "testing"
8 | "time"
9 | )
10 |
11 | func TestToComments(t *testing.T) {
12 | type args struct {
13 | commentsWithUser []model.Comment
14 | }
15 |
16 | comment1 := model.Comment{
17 | Content: "comment1",
18 | CreatedAt: time.Time{},
19 | ID: 1,
20 | LeftID: 0,
21 | LikeCount: 0,
22 | ParentID: 0,
23 | RightID: 0,
24 | Status: model.COMMENT_STATUS_REVIEWED,
25 | TopicID: 1,
26 | UpdatedAt: time.Time{},
27 | UserID: 1,
28 | User: model.User{
29 | CreatedAt: time.Time{},
30 | Email: "test1@gmail.com",
31 | ID: 1,
32 | Name: "tester",
33 | Password: "",
34 | UpdatedAt: time.Time{},
35 | },
36 | }
37 | comment2 := model.Comment{
38 | Content: "comment2",
39 | CreatedAt: time.Time{},
40 | ID: 2,
41 | LeftID: 0,
42 | LikeCount: 0,
43 | ParentID: 1,
44 | RightID: 0,
45 | Status: model.COMMENT_STATUS_REVIEWED,
46 | TopicID: 1,
47 | UpdatedAt: time.Time{},
48 | UserID: 2,
49 | User: model.User{
50 | CreatedAt: time.Time{},
51 | Email: "",
52 | ID: 2,
53 | Name: "tester2",
54 | Password: "",
55 | UpdatedAt: time.Time{},
56 | },
57 | }
58 | args1 := args{commentsWithUser: []model.Comment{comment1, comment2}}
59 |
60 | tests := []struct {
61 | name string
62 | args args
63 | want *models.Comments
64 | wantErr bool
65 | }{
66 | {
67 | name: "测试1",
68 | args: args1,
69 | want: nil,
70 | wantErr: false,
71 | },
72 |
73 | }
74 | for _, tt := range tests {
75 | t.Run(tt.name, func(t *testing.T) {
76 | got, err := ToComments(tt.args.commentsWithUser)
77 | if (err != nil) != tt.wantErr {
78 | t.Errorf("ToComments() error = %v, wantErr %v", err, tt.wantErr)
79 | return
80 | }
81 | if !reflect.DeepEqual(got, tt.want) {
82 | t.Errorf("ToComments() = %v, want %v", got, tt.want)
83 | }
84 | })
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/view/adapter/daily.go:
--------------------------------------------------------------------------------
1 | package adapter
2 |
3 | import (
4 | "backend/model"
5 | "backend/view/swagger/models"
6 | )
7 |
8 | // 将model.daily转换为输出
9 | func ToDaily(daily *model.Daily, content []model.Content) models.Daily {
10 | ret := models.Daily{
11 | Author: &daily.Author,
12 | CreatedAt: daily.CreatedAt.String(),
13 | Day: &daily.Day,
14 | ID: &daily.ID,
15 | Title: &daily.Title,
16 | }
17 |
18 | ret.Content = make([]*models.DailyContentItems, len(content))
19 | for i, item := range content {
20 | ret.Content[i] = &models.DailyContentItems{
21 | Comment: item.Comment,
22 | Link: item.Link,
23 | Title: item.Title,
24 | }
25 | }
26 | return ret
27 | }
--------------------------------------------------------------------------------
/view/adapter/tag.go:
--------------------------------------------------------------------------------
1 | package adapter
2 |
3 | import (
4 | "backend/model"
5 | "backend/view/swagger/models"
6 | )
7 |
8 | func ToTag(tag model.Tag) *models.Tag {
9 | out := models.Tag{
10 | ID: &tag.ID,
11 | Name: &tag.Name,
12 | TopicCount: &tag.TopicCount,
13 | }
14 | return &out
15 | }
16 |
17 |
18 | func ToTags(tags []model.Tag) []*models.Tag {
19 | out := make([]*models.Tag, len(tags))
20 | for i, tag := range tags {
21 | out[i] = ToTag(tag)
22 | }
23 | return out
24 | }
--------------------------------------------------------------------------------
/view/adapter/topic.go:
--------------------------------------------------------------------------------
1 | package adapter
2 |
3 | import (
4 | "backend/model"
5 | "backend/util"
6 | "backend/view/swagger/models"
7 | "github.com/go-openapi/strfmt"
8 | )
9 |
10 | // 根据用户信息获取用户
11 | func ToTopicSummarysByTopicsWithUser(topicsWithUser []model.Topic) models.TopicSummarys {
12 | topicSummaries := make([]*models.TopicSummary, len(topicsWithUser), len(topicsWithUser))
13 | for i, topic := range topicsWithUser{
14 | summary := ToTopicSummary(&topic)
15 | topicSummaries[i] = &summary
16 | }
17 | ret := models.TopicSummarys(topicSummaries)
18 | return ret
19 | }
20 |
21 | // 根据话题和点赞数组获取话题详情
22 | func ToTopicByTopicWithUser(topicWithUser *model.Topic, tags []model.Tag) models.Topic {
23 | t := topicWithUser.CreatedAt.Format(util.TIME_ISO_NO_T)
24 | t2 := float32(topicWithUser.Score)
25 | ret := models.Topic{
26 | Content: &topicWithUser.Content,
27 | CreatedAt: &t,
28 | ID: &topicWithUser.ID,
29 | LikeCount: &topicWithUser.LikeCount,
30 | Link: &topicWithUser.Link,
31 | Score: &t2,
32 | Source: &topicWithUser.Source,
33 | Title: &topicWithUser.Title,
34 | CommentCount: &topicWithUser.CommentCount,
35 | User: &models.UserSummary{
36 | ID: &topicWithUser.User.ID,
37 | Name: &topicWithUser.User.Name,
38 | SmallImage: nil,
39 | },
40 | }
41 |
42 | outTags := make([]*models.Tag, len(tags), len(tags))
43 | for i, tag := range tags {
44 | outTags[i] = ToTag(tag)
45 | }
46 |
47 | ret.Tags = outTags
48 | return ret
49 | }
50 |
51 | // 转换为TopicSummary
52 | func ToTopicSummary(topic *model.Topic) models.TopicSummary {
53 | contentSummary := topic.Content
54 | if len(topic.Content) > 100 {
55 | contentSummary = topic.Content[:100]
56 | }
57 | t1 := topic.CreatedAt.Format(util.TIME_ISO_NO_T)
58 | t3 := strfmt.URI(topic.Link)
59 | t4 := float32(topic.Score)
60 | summary := models.TopicSummary{
61 | ContentSummary: &contentSummary,
62 | CreatedAt: &t1,
63 | ID: &topic.ID,
64 | LikeCount: &topic.LikeCount,
65 | CommentCount: &topic.CommentCount,
66 | Link: &t3,
67 | Score: &t4,
68 | Source: &topic.Source,
69 | Title: &topic.Title,
70 | User: &models.UserSummary{
71 | ID: &topic.User.ID,
72 | Name: &topic.User.Name,
73 | SmallImage: nil,
74 | },
75 | }
76 | return summary
77 | }
--------------------------------------------------------------------------------
/view/adapter/user.go:
--------------------------------------------------------------------------------
1 | package adapter
2 |
3 | import (
4 | "backend/model"
5 | "backend/view/swagger/models"
6 | )
7 |
8 | // 转换成为UserSummary结构
9 | func ToUserSummary(user model.User) *models.UserSummary {
10 | t := "http://aaa"
11 | return &models.UserSummary{
12 | ID: &user.ID,
13 | Name: &user.Name,
14 | SmallImage: &t,
15 | }
16 | }
--------------------------------------------------------------------------------
/view/swagger/models/comment_detail.go:
--------------------------------------------------------------------------------
1 | // Code generated by go-swagger; DO NOT EDIT.
2 |
3 | package models
4 |
5 | // This file was generated by the swagger tool.
6 | // Editing this file might prove futile when you re-run the swagger generate command
7 |
8 | import (
9 | "strconv"
10 |
11 | strfmt "github.com/go-openapi/strfmt"
12 |
13 | "github.com/go-openapi/errors"
14 | "github.com/go-openapi/swag"
15 | "github.com/go-openapi/validate"
16 | )
17 |
18 | // CommentDetail 简要评论结构
19 | // swagger:model comment_detail
20 | type CommentDetail struct {
21 |
22 | // 子级别评论,最多只有两层,不会有更多层
23 | // Required: true
24 | Comments []*CommentSummary `json:"comments"`
25 |
26 | // 评论全部内容
27 | // Required: true
28 | Content *string `json:"content"`
29 |
30 | // 创建时间
31 | // Required: true
32 | CreatedAt *string `json:"created_at"`
33 |
34 | // 评论唯一标示
35 | // Required: true
36 | ID *int64 `json:"id"`
37 |
38 | // 点赞数
39 | // Required: true
40 | LikeCount *int64 `json:"like_count"`
41 |
42 | // 父级别评论的id
43 | // Required: true
44 | ParentID *int64 `json:"parent_id"`
45 |
46 | // user
47 | // Required: true
48 | User *UserSummary `json:"user"`
49 | }
50 |
51 | // Validate validates this comment detail
52 | func (m *CommentDetail) Validate(formats strfmt.Registry) error {
53 | var res []error
54 |
55 | if err := m.validateComments(formats); err != nil {
56 | res = append(res, err)
57 | }
58 |
59 | if err := m.validateContent(formats); err != nil {
60 | res = append(res, err)
61 | }
62 |
63 | if err := m.validateCreatedAt(formats); err != nil {
64 | res = append(res, err)
65 | }
66 |
67 | if err := m.validateID(formats); err != nil {
68 | res = append(res, err)
69 | }
70 |
71 | if err := m.validateLikeCount(formats); err != nil {
72 | res = append(res, err)
73 | }
74 |
75 | if err := m.validateParentID(formats); err != nil {
76 | res = append(res, err)
77 | }
78 |
79 | if err := m.validateUser(formats); err != nil {
80 | res = append(res, err)
81 | }
82 |
83 | if len(res) > 0 {
84 | return errors.CompositeValidationError(res...)
85 | }
86 | return nil
87 | }
88 |
89 | func (m *CommentDetail) validateComments(formats strfmt.Registry) error {
90 |
91 | if err := validate.Required("comments", "body", m.Comments); err != nil {
92 | return err
93 | }
94 |
95 | for i := 0; i < len(m.Comments); i++ {
96 | if swag.IsZero(m.Comments[i]) { // not required
97 | continue
98 | }
99 |
100 | if m.Comments[i] != nil {
101 | if err := m.Comments[i].Validate(formats); err != nil {
102 | if ve, ok := err.(*errors.Validation); ok {
103 | return ve.ValidateName("comments" + "." + strconv.Itoa(i))
104 | }
105 | return err
106 | }
107 | }
108 |
109 | }
110 |
111 | return nil
112 | }
113 |
114 | func (m *CommentDetail) validateContent(formats strfmt.Registry) error {
115 |
116 | if err := validate.Required("content", "body", m.Content); err != nil {
117 | return err
118 | }
119 |
120 | return nil
121 | }
122 |
123 | func (m *CommentDetail) validateCreatedAt(formats strfmt.Registry) error {
124 |
125 | if err := validate.Required("created_at", "body", m.CreatedAt); err != nil {
126 | return err
127 | }
128 |
129 | return nil
130 | }
131 |
132 | func (m *CommentDetail) validateID(formats strfmt.Registry) error {
133 |
134 | if err := validate.Required("id", "body", m.ID); err != nil {
135 | return err
136 | }
137 |
138 | return nil
139 | }
140 |
141 | func (m *CommentDetail) validateLikeCount(formats strfmt.Registry) error {
142 |
143 | if err := validate.Required("like_count", "body", m.LikeCount); err != nil {
144 | return err
145 | }
146 |
147 | return nil
148 | }
149 |
150 | func (m *CommentDetail) validateParentID(formats strfmt.Registry) error {
151 |
152 | if err := validate.Required("parent_id", "body", m.ParentID); err != nil {
153 | return err
154 | }
155 |
156 | return nil
157 | }
158 |
159 | func (m *CommentDetail) validateUser(formats strfmt.Registry) error {
160 |
161 | if err := validate.Required("user", "body", m.User); err != nil {
162 | return err
163 | }
164 |
165 | if m.User != nil {
166 | if err := m.User.Validate(formats); err != nil {
167 | if ve, ok := err.(*errors.Validation); ok {
168 | return ve.ValidateName("user")
169 | }
170 | return err
171 | }
172 | }
173 |
174 | return nil
175 | }
176 |
177 | // MarshalBinary interface implementation
178 | func (m *CommentDetail) MarshalBinary() ([]byte, error) {
179 | if m == nil {
180 | return nil, nil
181 | }
182 | return swag.WriteJSON(m)
183 | }
184 |
185 | // UnmarshalBinary interface implementation
186 | func (m *CommentDetail) UnmarshalBinary(b []byte) error {
187 | var res CommentDetail
188 | if err := swag.ReadJSON(b, &res); err != nil {
189 | return err
190 | }
191 | *m = res
192 | return nil
193 | }
194 |
--------------------------------------------------------------------------------
/view/swagger/models/comment_summary.go:
--------------------------------------------------------------------------------
1 | // Code generated by go-swagger; DO NOT EDIT.
2 |
3 | package models
4 |
5 | // This file was generated by the swagger tool.
6 | // Editing this file might prove futile when you re-run the swagger generate command
7 |
8 | import (
9 | strfmt "github.com/go-openapi/strfmt"
10 |
11 | "github.com/go-openapi/errors"
12 | "github.com/go-openapi/swag"
13 | "github.com/go-openapi/validate"
14 | )
15 |
16 | // CommentSummary 简要评论结构
17 | // swagger:model comment_summary
18 | type CommentSummary struct {
19 |
20 | // 评论全部内容
21 | // Required: true
22 | Content *string `json:"content"`
23 |
24 | // 创建时间
25 | // Required: true
26 | CreatedAt *string `json:"created_at"`
27 |
28 | // 评论唯一标示
29 | // Required: true
30 | ID *int64 `json:"id"`
31 |
32 | // 点赞数
33 | // Required: true
34 | LikeCount *int64 `json:"like_count"`
35 |
36 | // 父级别评论的id
37 | // Required: true
38 | ParentID *int64 `json:"parent_id"`
39 |
40 | // user
41 | // Required: true
42 | User *UserSummary `json:"user"`
43 | }
44 |
45 | // Validate validates this comment summary
46 | func (m *CommentSummary) Validate(formats strfmt.Registry) error {
47 | var res []error
48 |
49 | if err := m.validateContent(formats); err != nil {
50 | res = append(res, err)
51 | }
52 |
53 | if err := m.validateCreatedAt(formats); err != nil {
54 | res = append(res, err)
55 | }
56 |
57 | if err := m.validateID(formats); err != nil {
58 | res = append(res, err)
59 | }
60 |
61 | if err := m.validateLikeCount(formats); err != nil {
62 | res = append(res, err)
63 | }
64 |
65 | if err := m.validateParentID(formats); err != nil {
66 | res = append(res, err)
67 | }
68 |
69 | if err := m.validateUser(formats); err != nil {
70 | res = append(res, err)
71 | }
72 |
73 | if len(res) > 0 {
74 | return errors.CompositeValidationError(res...)
75 | }
76 | return nil
77 | }
78 |
79 | func (m *CommentSummary) validateContent(formats strfmt.Registry) error {
80 |
81 | if err := validate.Required("content", "body", m.Content); err != nil {
82 | return err
83 | }
84 |
85 | return nil
86 | }
87 |
88 | func (m *CommentSummary) validateCreatedAt(formats strfmt.Registry) error {
89 |
90 | if err := validate.Required("created_at", "body", m.CreatedAt); err != nil {
91 | return err
92 | }
93 |
94 | return nil
95 | }
96 |
97 | func (m *CommentSummary) validateID(formats strfmt.Registry) error {
98 |
99 | if err := validate.Required("id", "body", m.ID); err != nil {
100 | return err
101 | }
102 |
103 | return nil
104 | }
105 |
106 | func (m *CommentSummary) validateLikeCount(formats strfmt.Registry) error {
107 |
108 | if err := validate.Required("like_count", "body", m.LikeCount); err != nil {
109 | return err
110 | }
111 |
112 | return nil
113 | }
114 |
115 | func (m *CommentSummary) validateParentID(formats strfmt.Registry) error {
116 |
117 | if err := validate.Required("parent_id", "body", m.ParentID); err != nil {
118 | return err
119 | }
120 |
121 | return nil
122 | }
123 |
124 | func (m *CommentSummary) validateUser(formats strfmt.Registry) error {
125 |
126 | if err := validate.Required("user", "body", m.User); err != nil {
127 | return err
128 | }
129 |
130 | if m.User != nil {
131 | if err := m.User.Validate(formats); err != nil {
132 | if ve, ok := err.(*errors.Validation); ok {
133 | return ve.ValidateName("user")
134 | }
135 | return err
136 | }
137 | }
138 |
139 | return nil
140 | }
141 |
142 | // MarshalBinary interface implementation
143 | func (m *CommentSummary) MarshalBinary() ([]byte, error) {
144 | if m == nil {
145 | return nil, nil
146 | }
147 | return swag.WriteJSON(m)
148 | }
149 |
150 | // UnmarshalBinary interface implementation
151 | func (m *CommentSummary) UnmarshalBinary(b []byte) error {
152 | var res CommentSummary
153 | if err := swag.ReadJSON(b, &res); err != nil {
154 | return err
155 | }
156 | *m = res
157 | return nil
158 | }
159 |
--------------------------------------------------------------------------------
/view/swagger/models/comments.go:
--------------------------------------------------------------------------------
1 | // Code generated by go-swagger; DO NOT EDIT.
2 |
3 | package models
4 |
5 | // This file was generated by the swagger tool.
6 | // Editing this file might prove futile when you re-run the swagger generate command
7 |
8 | import (
9 | "strconv"
10 |
11 | strfmt "github.com/go-openapi/strfmt"
12 |
13 | "github.com/go-openapi/errors"
14 | "github.com/go-openapi/swag"
15 | )
16 |
17 | // Comments 评论详情列表
18 | // swagger:model comments
19 | type Comments []*CommentDetail
20 |
21 | // Validate validates this comments
22 | func (m Comments) Validate(formats strfmt.Registry) error {
23 | var res []error
24 |
25 | for i := 0; i < len(m); i++ {
26 | if swag.IsZero(m[i]) { // not required
27 | continue
28 | }
29 |
30 | if m[i] != nil {
31 | if err := m[i].Validate(formats); err != nil {
32 | if ve, ok := err.(*errors.Validation); ok {
33 | return ve.ValidateName(strconv.Itoa(i))
34 | }
35 | return err
36 | }
37 | }
38 |
39 | }
40 |
41 | if len(res) > 0 {
42 | return errors.CompositeValidationError(res...)
43 | }
44 | return nil
45 | }
46 |
--------------------------------------------------------------------------------
/view/swagger/models/daily.go:
--------------------------------------------------------------------------------
1 | // Code generated by go-swagger; DO NOT EDIT.
2 |
3 | package models
4 |
5 | // This file was generated by the swagger tool.
6 | // Editing this file might prove futile when you re-run the swagger generate command
7 |
8 | import (
9 | "strconv"
10 |
11 | strfmt "github.com/go-openapi/strfmt"
12 |
13 | "github.com/go-openapi/errors"
14 | "github.com/go-openapi/swag"
15 | "github.com/go-openapi/validate"
16 | )
17 |
18 | // Daily 本日知识点结构
19 | // swagger:model daily
20 | type Daily struct {
21 |
22 | // 作者
23 | // Required: true
24 | Author *string `json:"author"`
25 |
26 | // 具体今日知识点的内容
27 | // Required: true
28 | Content []*DailyContentItems `json:"content"`
29 |
30 | // 创建时间
31 | CreatedAt string `json:"created_at,omitempty"`
32 |
33 | // 日期
34 | // Required: true
35 | Day *string `json:"day"`
36 |
37 | // 每日唯一标示
38 | // Required: true
39 | ID *int64 `json:"id"`
40 |
41 | // 知识点标题
42 | // Required: true
43 | Title *string `json:"title"`
44 | }
45 |
46 | // Validate validates this daily
47 | func (m *Daily) Validate(formats strfmt.Registry) error {
48 | var res []error
49 |
50 | if err := m.validateAuthor(formats); err != nil {
51 | res = append(res, err)
52 | }
53 |
54 | if err := m.validateContent(formats); err != nil {
55 | res = append(res, err)
56 | }
57 |
58 | if err := m.validateDay(formats); err != nil {
59 | res = append(res, err)
60 | }
61 |
62 | if err := m.validateID(formats); err != nil {
63 | res = append(res, err)
64 | }
65 |
66 | if err := m.validateTitle(formats); err != nil {
67 | res = append(res, err)
68 | }
69 |
70 | if len(res) > 0 {
71 | return errors.CompositeValidationError(res...)
72 | }
73 | return nil
74 | }
75 |
76 | func (m *Daily) validateAuthor(formats strfmt.Registry) error {
77 |
78 | if err := validate.Required("author", "body", m.Author); err != nil {
79 | return err
80 | }
81 |
82 | return nil
83 | }
84 |
85 | func (m *Daily) validateContent(formats strfmt.Registry) error {
86 |
87 | if err := validate.Required("content", "body", m.Content); err != nil {
88 | return err
89 | }
90 |
91 | for i := 0; i < len(m.Content); i++ {
92 | if swag.IsZero(m.Content[i]) { // not required
93 | continue
94 | }
95 |
96 | if m.Content[i] != nil {
97 | if err := m.Content[i].Validate(formats); err != nil {
98 | if ve, ok := err.(*errors.Validation); ok {
99 | return ve.ValidateName("content" + "." + strconv.Itoa(i))
100 | }
101 | return err
102 | }
103 | }
104 |
105 | }
106 |
107 | return nil
108 | }
109 |
110 | func (m *Daily) validateDay(formats strfmt.Registry) error {
111 |
112 | if err := validate.Required("day", "body", m.Day); err != nil {
113 | return err
114 | }
115 |
116 | return nil
117 | }
118 |
119 | func (m *Daily) validateID(formats strfmt.Registry) error {
120 |
121 | if err := validate.Required("id", "body", m.ID); err != nil {
122 | return err
123 | }
124 |
125 | return nil
126 | }
127 |
128 | func (m *Daily) validateTitle(formats strfmt.Registry) error {
129 |
130 | if err := validate.Required("title", "body", m.Title); err != nil {
131 | return err
132 | }
133 |
134 | return nil
135 | }
136 |
137 | // MarshalBinary interface implementation
138 | func (m *Daily) MarshalBinary() ([]byte, error) {
139 | if m == nil {
140 | return nil, nil
141 | }
142 | return swag.WriteJSON(m)
143 | }
144 |
145 | // UnmarshalBinary interface implementation
146 | func (m *Daily) UnmarshalBinary(b []byte) error {
147 | var res Daily
148 | if err := swag.ReadJSON(b, &res); err != nil {
149 | return err
150 | }
151 | *m = res
152 | return nil
153 | }
154 |
--------------------------------------------------------------------------------
/view/swagger/models/daily_content_items.go:
--------------------------------------------------------------------------------
1 | // Code generated by go-swagger; DO NOT EDIT.
2 |
3 | package models
4 |
5 | // This file was generated by the swagger tool.
6 | // Editing this file might prove futile when you re-run the swagger generate command
7 |
8 | import (
9 | strfmt "github.com/go-openapi/strfmt"
10 |
11 | "github.com/go-openapi/swag"
12 | )
13 |
14 | // DailyContentItems daily content items
15 | // swagger:model dailyContentItems
16 | type DailyContentItems struct {
17 |
18 | // comment
19 | Comment string `json:"comment,omitempty"`
20 |
21 | // link
22 | Link string `json:"link,omitempty"`
23 |
24 | // title
25 | Title string `json:"title,omitempty"`
26 | }
27 |
28 | // Validate validates this daily content items
29 | func (m *DailyContentItems) Validate(formats strfmt.Registry) error {
30 | return nil
31 | }
32 |
33 | // MarshalBinary interface implementation
34 | func (m *DailyContentItems) MarshalBinary() ([]byte, error) {
35 | if m == nil {
36 | return nil, nil
37 | }
38 | return swag.WriteJSON(m)
39 | }
40 |
41 | // UnmarshalBinary interface implementation
42 | func (m *DailyContentItems) UnmarshalBinary(b []byte) error {
43 | var res DailyContentItems
44 | if err := swag.ReadJSON(b, &res); err != nil {
45 | return err
46 | }
47 | *m = res
48 | return nil
49 | }
50 |
--------------------------------------------------------------------------------
/view/swagger/models/error_response.go:
--------------------------------------------------------------------------------
1 | // Code generated by go-swagger; DO NOT EDIT.
2 |
3 | package models
4 |
5 | // This file was generated by the swagger tool.
6 | // Editing this file might prove futile when you re-run the swagger generate command
7 |
8 | import (
9 | strfmt "github.com/go-openapi/strfmt"
10 |
11 | "github.com/go-openapi/errors"
12 | "github.com/go-openapi/swag"
13 | "github.com/go-openapi/validate"
14 | )
15 |
16 | // ErrorResponse 错误返回信息
17 | // swagger:model error_response
18 | type ErrorResponse struct {
19 |
20 | // 自定义错误码
21 | // Required: true
22 | Code *int64 `json:"code"`
23 |
24 | // 错误信息
25 | // Required: true
26 | Msg *string `json:"msg"`
27 | }
28 |
29 | // Validate validates this error response
30 | func (m *ErrorResponse) Validate(formats strfmt.Registry) error {
31 | var res []error
32 |
33 | if err := m.validateCode(formats); err != nil {
34 | res = append(res, err)
35 | }
36 |
37 | if err := m.validateMsg(formats); err != nil {
38 | res = append(res, err)
39 | }
40 |
41 | if len(res) > 0 {
42 | return errors.CompositeValidationError(res...)
43 | }
44 | return nil
45 | }
46 |
47 | func (m *ErrorResponse) validateCode(formats strfmt.Registry) error {
48 |
49 | if err := validate.Required("code", "body", m.Code); err != nil {
50 | return err
51 | }
52 |
53 | return nil
54 | }
55 |
56 | func (m *ErrorResponse) validateMsg(formats strfmt.Registry) error {
57 |
58 | if err := validate.Required("msg", "body", m.Msg); err != nil {
59 | return err
60 | }
61 |
62 | return nil
63 | }
64 |
65 | // MarshalBinary interface implementation
66 | func (m *ErrorResponse) MarshalBinary() ([]byte, error) {
67 | if m == nil {
68 | return nil, nil
69 | }
70 | return swag.WriteJSON(m)
71 | }
72 |
73 | // UnmarshalBinary interface implementation
74 | func (m *ErrorResponse) UnmarshalBinary(b []byte) error {
75 | var res ErrorResponse
76 | if err := swag.ReadJSON(b, &res); err != nil {
77 | return err
78 | }
79 | *m = res
80 | return nil
81 | }
82 |
--------------------------------------------------------------------------------
/view/swagger/models/get_tag_topics_o_k_body.go:
--------------------------------------------------------------------------------
1 | // Code generated by go-swagger; DO NOT EDIT.
2 |
3 | package models
4 |
5 | // This file was generated by the swagger tool.
6 | // Editing this file might prove futile when you re-run the swagger generate command
7 |
8 | import (
9 | strfmt "github.com/go-openapi/strfmt"
10 |
11 | "github.com/go-openapi/errors"
12 | "github.com/go-openapi/swag"
13 | )
14 |
15 | // GetTagTopicsOKBody get tag topics o k body
16 | // swagger:model getTagTopicsOKBody
17 | type GetTagTopicsOKBody struct {
18 |
19 | // pager
20 | Pager *Pager `json:"pager,omitempty"`
21 |
22 | // topics
23 | Topics TopicSummarys `json:"topics,omitempty"`
24 | }
25 |
26 | // Validate validates this get tag topics o k body
27 | func (m *GetTagTopicsOKBody) Validate(formats strfmt.Registry) error {
28 | var res []error
29 |
30 | if err := m.validatePager(formats); err != nil {
31 | res = append(res, err)
32 | }
33 |
34 | if err := m.validateTopics(formats); err != nil {
35 | res = append(res, err)
36 | }
37 |
38 | if len(res) > 0 {
39 | return errors.CompositeValidationError(res...)
40 | }
41 | return nil
42 | }
43 |
44 | func (m *GetTagTopicsOKBody) validatePager(formats strfmt.Registry) error {
45 |
46 | if swag.IsZero(m.Pager) { // not required
47 | return nil
48 | }
49 |
50 | if m.Pager != nil {
51 | if err := m.Pager.Validate(formats); err != nil {
52 | if ve, ok := err.(*errors.Validation); ok {
53 | return ve.ValidateName("pager")
54 | }
55 | return err
56 | }
57 | }
58 |
59 | return nil
60 | }
61 |
62 | func (m *GetTagTopicsOKBody) validateTopics(formats strfmt.Registry) error {
63 |
64 | if swag.IsZero(m.Topics) { // not required
65 | return nil
66 | }
67 |
68 | if err := m.Topics.Validate(formats); err != nil {
69 | if ve, ok := err.(*errors.Validation); ok {
70 | return ve.ValidateName("topics")
71 | }
72 | return err
73 | }
74 |
75 | return nil
76 | }
77 |
78 | // MarshalBinary interface implementation
79 | func (m *GetTagTopicsOKBody) MarshalBinary() ([]byte, error) {
80 | if m == nil {
81 | return nil, nil
82 | }
83 | return swag.WriteJSON(m)
84 | }
85 |
86 | // UnmarshalBinary interface implementation
87 | func (m *GetTagTopicsOKBody) UnmarshalBinary(b []byte) error {
88 | var res GetTagTopicsOKBody
89 | if err := swag.ReadJSON(b, &res); err != nil {
90 | return err
91 | }
92 | *m = res
93 | return nil
94 | }
95 |
--------------------------------------------------------------------------------
/view/swagger/models/get_topic_list_o_k_body.go:
--------------------------------------------------------------------------------
1 | // Code generated by go-swagger; DO NOT EDIT.
2 |
3 | package models
4 |
5 | // This file was generated by the swagger tool.
6 | // Editing this file might prove futile when you re-run the swagger generate command
7 |
8 | import (
9 | strfmt "github.com/go-openapi/strfmt"
10 |
11 | "github.com/go-openapi/errors"
12 | "github.com/go-openapi/swag"
13 | )
14 |
15 | // GetTopicListOKBody get topic list o k body
16 | // swagger:model getTopicListOKBody
17 | type GetTopicListOKBody struct {
18 |
19 | // pager
20 | Pager *Pager `json:"pager,omitempty"`
21 |
22 | // topics
23 | Topics TopicSummarys `json:"topics,omitempty"`
24 | }
25 |
26 | // Validate validates this get topic list o k body
27 | func (m *GetTopicListOKBody) Validate(formats strfmt.Registry) error {
28 | var res []error
29 |
30 | if err := m.validatePager(formats); err != nil {
31 | res = append(res, err)
32 | }
33 |
34 | if err := m.validateTopics(formats); err != nil {
35 | res = append(res, err)
36 | }
37 |
38 | if len(res) > 0 {
39 | return errors.CompositeValidationError(res...)
40 | }
41 | return nil
42 | }
43 |
44 | func (m *GetTopicListOKBody) validatePager(formats strfmt.Registry) error {
45 |
46 | if swag.IsZero(m.Pager) { // not required
47 | return nil
48 | }
49 |
50 | if m.Pager != nil {
51 | if err := m.Pager.Validate(formats); err != nil {
52 | if ve, ok := err.(*errors.Validation); ok {
53 | return ve.ValidateName("pager")
54 | }
55 | return err
56 | }
57 | }
58 |
59 | return nil
60 | }
61 |
62 | func (m *GetTopicListOKBody) validateTopics(formats strfmt.Registry) error {
63 |
64 | if swag.IsZero(m.Topics) { // not required
65 | return nil
66 | }
67 |
68 | if err := m.Topics.Validate(formats); err != nil {
69 | if ve, ok := err.(*errors.Validation); ok {
70 | return ve.ValidateName("topics")
71 | }
72 | return err
73 | }
74 |
75 | return nil
76 | }
77 |
78 | // MarshalBinary interface implementation
79 | func (m *GetTopicListOKBody) MarshalBinary() ([]byte, error) {
80 | if m == nil {
81 | return nil, nil
82 | }
83 | return swag.WriteJSON(m)
84 | }
85 |
86 | // UnmarshalBinary interface implementation
87 | func (m *GetTopicListOKBody) UnmarshalBinary(b []byte) error {
88 | var res GetTopicListOKBody
89 | if err := swag.ReadJSON(b, &res); err != nil {
90 | return err
91 | }
92 | *m = res
93 | return nil
94 | }
95 |
--------------------------------------------------------------------------------
/view/swagger/models/pager.go:
--------------------------------------------------------------------------------
1 | // Code generated by go-swagger; DO NOT EDIT.
2 |
3 | package models
4 |
5 | // This file was generated by the swagger tool.
6 | // Editing this file might prove futile when you re-run the swagger generate command
7 |
8 | import (
9 | strfmt "github.com/go-openapi/strfmt"
10 |
11 | "github.com/go-openapi/errors"
12 | "github.com/go-openapi/swag"
13 | "github.com/go-openapi/validate"
14 | )
15 |
16 | // Pager 分页信息
17 | // swagger:model pager
18 | type Pager struct {
19 |
20 | // 当前页面
21 | // Required: true
22 | Page *int64 `json:"page"`
23 |
24 | // 单页个数
25 | // Required: true
26 | Size *int64 `json:"size"`
27 |
28 | // 总共页面数
29 | // Required: true
30 | TotalPage *int64 `json:"total_page"`
31 | }
32 |
33 | // Validate validates this pager
34 | func (m *Pager) Validate(formats strfmt.Registry) error {
35 | var res []error
36 |
37 | if err := m.validatePage(formats); err != nil {
38 | res = append(res, err)
39 | }
40 |
41 | if err := m.validateSize(formats); err != nil {
42 | res = append(res, err)
43 | }
44 |
45 | if err := m.validateTotalPage(formats); err != nil {
46 | res = append(res, err)
47 | }
48 |
49 | if len(res) > 0 {
50 | return errors.CompositeValidationError(res...)
51 | }
52 | return nil
53 | }
54 |
55 | func (m *Pager) validatePage(formats strfmt.Registry) error {
56 |
57 | if err := validate.Required("page", "body", m.Page); err != nil {
58 | return err
59 | }
60 |
61 | return nil
62 | }
63 |
64 | func (m *Pager) validateSize(formats strfmt.Registry) error {
65 |
66 | if err := validate.Required("size", "body", m.Size); err != nil {
67 | return err
68 | }
69 |
70 | return nil
71 | }
72 |
73 | func (m *Pager) validateTotalPage(formats strfmt.Registry) error {
74 |
75 | if err := validate.Required("total_page", "body", m.TotalPage); err != nil {
76 | return err
77 | }
78 |
79 | return nil
80 | }
81 |
82 | // MarshalBinary interface implementation
83 | func (m *Pager) MarshalBinary() ([]byte, error) {
84 | if m == nil {
85 | return nil, nil
86 | }
87 | return swag.WriteJSON(m)
88 | }
89 |
90 | // UnmarshalBinary interface implementation
91 | func (m *Pager) UnmarshalBinary(b []byte) error {
92 | var res Pager
93 | if err := swag.ReadJSON(b, &res); err != nil {
94 | return err
95 | }
96 | *m = res
97 | return nil
98 | }
99 |
--------------------------------------------------------------------------------
/view/swagger/models/tag.go:
--------------------------------------------------------------------------------
1 | // Code generated by go-swagger; DO NOT EDIT.
2 |
3 | package models
4 |
5 | // This file was generated by the swagger tool.
6 | // Editing this file might prove futile when you re-run the swagger generate command
7 |
8 | import (
9 | strfmt "github.com/go-openapi/strfmt"
10 |
11 | "github.com/go-openapi/errors"
12 | "github.com/go-openapi/swag"
13 | "github.com/go-openapi/validate"
14 | )
15 |
16 | // Tag 标签
17 | // swagger:model tag
18 | type Tag struct {
19 |
20 | // 标签唯一标示
21 | // Required: true
22 | ID *int64 `json:"id"`
23 |
24 | // 标签名称
25 | // Required: true
26 | Name *string `json:"name"`
27 |
28 | // 话题数
29 | // Required: true
30 | TopicCount *int64 `json:"topic_count"`
31 | }
32 |
33 | // Validate validates this tag
34 | func (m *Tag) Validate(formats strfmt.Registry) error {
35 | var res []error
36 |
37 | if err := m.validateID(formats); err != nil {
38 | res = append(res, err)
39 | }
40 |
41 | if err := m.validateName(formats); err != nil {
42 | res = append(res, err)
43 | }
44 |
45 | if err := m.validateTopicCount(formats); err != nil {
46 | res = append(res, err)
47 | }
48 |
49 | if len(res) > 0 {
50 | return errors.CompositeValidationError(res...)
51 | }
52 | return nil
53 | }
54 |
55 | func (m *Tag) validateID(formats strfmt.Registry) error {
56 |
57 | if err := validate.Required("id", "body", m.ID); err != nil {
58 | return err
59 | }
60 |
61 | return nil
62 | }
63 |
64 | func (m *Tag) validateName(formats strfmt.Registry) error {
65 |
66 | if err := validate.Required("name", "body", m.Name); err != nil {
67 | return err
68 | }
69 |
70 | return nil
71 | }
72 |
73 | func (m *Tag) validateTopicCount(formats strfmt.Registry) error {
74 |
75 | if err := validate.Required("topic_count", "body", m.TopicCount); err != nil {
76 | return err
77 | }
78 |
79 | return nil
80 | }
81 |
82 | // MarshalBinary interface implementation
83 | func (m *Tag) MarshalBinary() ([]byte, error) {
84 | if m == nil {
85 | return nil, nil
86 | }
87 | return swag.WriteJSON(m)
88 | }
89 |
90 | // UnmarshalBinary interface implementation
91 | func (m *Tag) UnmarshalBinary(b []byte) error {
92 | var res Tag
93 | if err := swag.ReadJSON(b, &res); err != nil {
94 | return err
95 | }
96 | *m = res
97 | return nil
98 | }
99 |
--------------------------------------------------------------------------------
/view/swagger/models/tags.go:
--------------------------------------------------------------------------------
1 | // Code generated by go-swagger; DO NOT EDIT.
2 |
3 | package models
4 |
5 | // This file was generated by the swagger tool.
6 | // Editing this file might prove futile when you re-run the swagger generate command
7 |
8 | import (
9 | "strconv"
10 |
11 | strfmt "github.com/go-openapi/strfmt"
12 |
13 | "github.com/go-openapi/errors"
14 | "github.com/go-openapi/swag"
15 | )
16 |
17 | // Tags 标签列表
18 | // swagger:model tags
19 | type Tags []*Tag
20 |
21 | // Validate validates this tags
22 | func (m Tags) Validate(formats strfmt.Registry) error {
23 | var res []error
24 |
25 | for i := 0; i < len(m); i++ {
26 | if swag.IsZero(m[i]) { // not required
27 | continue
28 | }
29 |
30 | if m[i] != nil {
31 | if err := m[i].Validate(formats); err != nil {
32 | if ve, ok := err.(*errors.Validation); ok {
33 | return ve.ValidateName(strconv.Itoa(i))
34 | }
35 | return err
36 | }
37 | }
38 |
39 | }
40 |
41 | if len(res) > 0 {
42 | return errors.CompositeValidationError(res...)
43 | }
44 | return nil
45 | }
46 |
--------------------------------------------------------------------------------
/view/swagger/models/topic.go:
--------------------------------------------------------------------------------
1 | // Code generated by go-swagger; DO NOT EDIT.
2 |
3 | package models
4 |
5 | // This file was generated by the swagger tool.
6 | // Editing this file might prove futile when you re-run the swagger generate command
7 |
8 | import (
9 | "encoding/json"
10 | "strconv"
11 |
12 | strfmt "github.com/go-openapi/strfmt"
13 |
14 | "github.com/go-openapi/errors"
15 | "github.com/go-openapi/swag"
16 | "github.com/go-openapi/validate"
17 | )
18 |
19 | // Topic 简要话题结构
20 | // swagger:model topic
21 | type Topic struct {
22 |
23 | // 评论数
24 | // Required: true
25 | CommentCount *int64 `json:"comment_count"`
26 |
27 | // 话题的全部内容
28 | // Required: true
29 | Content *string `json:"content"`
30 |
31 | // 创建时间
32 | // Required: true
33 | CreatedAt *string `json:"created_at"`
34 |
35 | // 话题唯一标识别
36 | // Required: true
37 | ID *int64 `json:"id"`
38 |
39 | // 点赞数
40 | // Required: true
41 | LikeCount *int64 `json:"like_count"`
42 |
43 | // 话题外部链接,可能为空
44 | // Required: true
45 | Link *string `json:"link"`
46 |
47 | // 话题评分
48 | // Required: true
49 | Score *float32 `json:"score"`
50 |
51 | // 话题来源
52 | // Required: true
53 | // Enum: [论坛 自发]
54 | Source *string `json:"source"`
55 |
56 | // tags
57 | // Required: true
58 | Tags []*Tag `json:"tags"`
59 |
60 | // 话题的标题
61 | // Required: true
62 | Title *string `json:"title"`
63 |
64 | // user
65 | // Required: true
66 | User *UserSummary `json:"user"`
67 | }
68 |
69 | // Validate validates this topic
70 | func (m *Topic) Validate(formats strfmt.Registry) error {
71 | var res []error
72 |
73 | if err := m.validateCommentCount(formats); err != nil {
74 | res = append(res, err)
75 | }
76 |
77 | if err := m.validateContent(formats); err != nil {
78 | res = append(res, err)
79 | }
80 |
81 | if err := m.validateCreatedAt(formats); err != nil {
82 | res = append(res, err)
83 | }
84 |
85 | if err := m.validateID(formats); err != nil {
86 | res = append(res, err)
87 | }
88 |
89 | if err := m.validateLikeCount(formats); err != nil {
90 | res = append(res, err)
91 | }
92 |
93 | if err := m.validateLink(formats); err != nil {
94 | res = append(res, err)
95 | }
96 |
97 | if err := m.validateScore(formats); err != nil {
98 | res = append(res, err)
99 | }
100 |
101 | if err := m.validateSource(formats); err != nil {
102 | res = append(res, err)
103 | }
104 |
105 | if err := m.validateTags(formats); err != nil {
106 | res = append(res, err)
107 | }
108 |
109 | if err := m.validateTitle(formats); err != nil {
110 | res = append(res, err)
111 | }
112 |
113 | if err := m.validateUser(formats); err != nil {
114 | res = append(res, err)
115 | }
116 |
117 | if len(res) > 0 {
118 | return errors.CompositeValidationError(res...)
119 | }
120 | return nil
121 | }
122 |
123 | func (m *Topic) validateCommentCount(formats strfmt.Registry) error {
124 |
125 | if err := validate.Required("comment_count", "body", m.CommentCount); err != nil {
126 | return err
127 | }
128 |
129 | return nil
130 | }
131 |
132 | func (m *Topic) validateContent(formats strfmt.Registry) error {
133 |
134 | if err := validate.Required("content", "body", m.Content); err != nil {
135 | return err
136 | }
137 |
138 | return nil
139 | }
140 |
141 | func (m *Topic) validateCreatedAt(formats strfmt.Registry) error {
142 |
143 | if err := validate.Required("created_at", "body", m.CreatedAt); err != nil {
144 | return err
145 | }
146 |
147 | return nil
148 | }
149 |
150 | func (m *Topic) validateID(formats strfmt.Registry) error {
151 |
152 | if err := validate.Required("id", "body", m.ID); err != nil {
153 | return err
154 | }
155 |
156 | return nil
157 | }
158 |
159 | func (m *Topic) validateLikeCount(formats strfmt.Registry) error {
160 |
161 | if err := validate.Required("like_count", "body", m.LikeCount); err != nil {
162 | return err
163 | }
164 |
165 | return nil
166 | }
167 |
168 | func (m *Topic) validateLink(formats strfmt.Registry) error {
169 |
170 | if err := validate.Required("link", "body", m.Link); err != nil {
171 | return err
172 | }
173 |
174 | return nil
175 | }
176 |
177 | func (m *Topic) validateScore(formats strfmt.Registry) error {
178 |
179 | if err := validate.Required("score", "body", m.Score); err != nil {
180 | return err
181 | }
182 |
183 | return nil
184 | }
185 |
186 | var topicTypeSourcePropEnum []interface{}
187 |
188 | func init() {
189 | var res []string
190 | if err := json.Unmarshal([]byte(`["论坛","自发"]`), &res); err != nil {
191 | panic(err)
192 | }
193 | for _, v := range res {
194 | topicTypeSourcePropEnum = append(topicTypeSourcePropEnum, v)
195 | }
196 | }
197 |
198 | const (
199 |
200 | // TopicSourceX论坛 captures enum value "论坛"
201 | TopicSourceX论坛 string = "论坛"
202 |
203 | // TopicSourceX自发 captures enum value "自发"
204 | TopicSourceX自发 string = "自发"
205 | )
206 |
207 | // prop value enum
208 | func (m *Topic) validateSourceEnum(path, location string, value string) error {
209 | if err := validate.Enum(path, location, value, topicTypeSourcePropEnum); err != nil {
210 | return err
211 | }
212 | return nil
213 | }
214 |
215 | func (m *Topic) validateSource(formats strfmt.Registry) error {
216 |
217 | if err := validate.Required("source", "body", m.Source); err != nil {
218 | return err
219 | }
220 |
221 | // value enum
222 | if err := m.validateSourceEnum("source", "body", *m.Source); err != nil {
223 | return err
224 | }
225 |
226 | return nil
227 | }
228 |
229 | func (m *Topic) validateTags(formats strfmt.Registry) error {
230 |
231 | if err := validate.Required("tags", "body", m.Tags); err != nil {
232 | return err
233 | }
234 |
235 | for i := 0; i < len(m.Tags); i++ {
236 | if swag.IsZero(m.Tags[i]) { // not required
237 | continue
238 | }
239 |
240 | if m.Tags[i] != nil {
241 | if err := m.Tags[i].Validate(formats); err != nil {
242 | if ve, ok := err.(*errors.Validation); ok {
243 | return ve.ValidateName("tags" + "." + strconv.Itoa(i))
244 | }
245 | return err
246 | }
247 | }
248 |
249 | }
250 |
251 | return nil
252 | }
253 |
254 | func (m *Topic) validateTitle(formats strfmt.Registry) error {
255 |
256 | if err := validate.Required("title", "body", m.Title); err != nil {
257 | return err
258 | }
259 |
260 | return nil
261 | }
262 |
263 | func (m *Topic) validateUser(formats strfmt.Registry) error {
264 |
265 | if err := validate.Required("user", "body", m.User); err != nil {
266 | return err
267 | }
268 |
269 | if m.User != nil {
270 | if err := m.User.Validate(formats); err != nil {
271 | if ve, ok := err.(*errors.Validation); ok {
272 | return ve.ValidateName("user")
273 | }
274 | return err
275 | }
276 | }
277 |
278 | return nil
279 | }
280 |
281 | // MarshalBinary interface implementation
282 | func (m *Topic) MarshalBinary() ([]byte, error) {
283 | if m == nil {
284 | return nil, nil
285 | }
286 | return swag.WriteJSON(m)
287 | }
288 |
289 | // UnmarshalBinary interface implementation
290 | func (m *Topic) UnmarshalBinary(b []byte) error {
291 | var res Topic
292 | if err := swag.ReadJSON(b, &res); err != nil {
293 | return err
294 | }
295 | *m = res
296 | return nil
297 | }
298 |
--------------------------------------------------------------------------------
/view/swagger/models/topic_summary.go:
--------------------------------------------------------------------------------
1 | // Code generated by go-swagger; DO NOT EDIT.
2 |
3 | package models
4 |
5 | // This file was generated by the swagger tool.
6 | // Editing this file might prove futile when you re-run the swagger generate command
7 |
8 | import (
9 | "encoding/json"
10 |
11 | strfmt "github.com/go-openapi/strfmt"
12 |
13 | "github.com/go-openapi/errors"
14 | "github.com/go-openapi/swag"
15 | "github.com/go-openapi/validate"
16 | )
17 |
18 | // TopicSummary 简要话题结构
19 | // swagger:model topic_summary
20 | type TopicSummary struct {
21 |
22 | // 评论数
23 | // Required: true
24 | CommentCount *int64 `json:"comment_count"`
25 |
26 | // 话题的内容概要,简略至100字以内
27 | // Required: true
28 | ContentSummary *string `json:"content_summary"`
29 |
30 | // 创建时间
31 | // Required: true
32 | CreatedAt *string `json:"created_at"`
33 |
34 | // 话题唯一标识别
35 | // Required: true
36 | ID *int64 `json:"id"`
37 |
38 | // 点赞数
39 | // Required: true
40 | LikeCount *int64 `json:"like_count"`
41 |
42 | // 话题外部链接,可能为空
43 | // Required: true
44 | // Format: uri
45 | Link *strfmt.URI `json:"link"`
46 |
47 | // 话题评分
48 | // Required: true
49 | Score *float32 `json:"score"`
50 |
51 | // 话题来源
52 | // Required: true
53 | // Enum: [论坛 自发]
54 | Source *string `json:"source"`
55 |
56 | // 话题的标题
57 | // Required: true
58 | Title *string `json:"title"`
59 |
60 | // user
61 | // Required: true
62 | User *UserSummary `json:"user"`
63 | }
64 |
65 | // Validate validates this topic summary
66 | func (m *TopicSummary) Validate(formats strfmt.Registry) error {
67 | var res []error
68 |
69 | if err := m.validateCommentCount(formats); err != nil {
70 | res = append(res, err)
71 | }
72 |
73 | if err := m.validateContentSummary(formats); err != nil {
74 | res = append(res, err)
75 | }
76 |
77 | if err := m.validateCreatedAt(formats); err != nil {
78 | res = append(res, err)
79 | }
80 |
81 | if err := m.validateID(formats); err != nil {
82 | res = append(res, err)
83 | }
84 |
85 | if err := m.validateLikeCount(formats); err != nil {
86 | res = append(res, err)
87 | }
88 |
89 | if err := m.validateLink(formats); err != nil {
90 | res = append(res, err)
91 | }
92 |
93 | if err := m.validateScore(formats); err != nil {
94 | res = append(res, err)
95 | }
96 |
97 | if err := m.validateSource(formats); err != nil {
98 | res = append(res, err)
99 | }
100 |
101 | if err := m.validateTitle(formats); err != nil {
102 | res = append(res, err)
103 | }
104 |
105 | if err := m.validateUser(formats); err != nil {
106 | res = append(res, err)
107 | }
108 |
109 | if len(res) > 0 {
110 | return errors.CompositeValidationError(res...)
111 | }
112 | return nil
113 | }
114 |
115 | func (m *TopicSummary) validateCommentCount(formats strfmt.Registry) error {
116 |
117 | if err := validate.Required("comment_count", "body", m.CommentCount); err != nil {
118 | return err
119 | }
120 |
121 | return nil
122 | }
123 |
124 | func (m *TopicSummary) validateContentSummary(formats strfmt.Registry) error {
125 |
126 | if err := validate.Required("content_summary", "body", m.ContentSummary); err != nil {
127 | return err
128 | }
129 |
130 | return nil
131 | }
132 |
133 | func (m *TopicSummary) validateCreatedAt(formats strfmt.Registry) error {
134 |
135 | if err := validate.Required("created_at", "body", m.CreatedAt); err != nil {
136 | return err
137 | }
138 |
139 | return nil
140 | }
141 |
142 | func (m *TopicSummary) validateID(formats strfmt.Registry) error {
143 |
144 | if err := validate.Required("id", "body", m.ID); err != nil {
145 | return err
146 | }
147 |
148 | return nil
149 | }
150 |
151 | func (m *TopicSummary) validateLikeCount(formats strfmt.Registry) error {
152 |
153 | if err := validate.Required("like_count", "body", m.LikeCount); err != nil {
154 | return err
155 | }
156 |
157 | return nil
158 | }
159 |
160 | func (m *TopicSummary) validateLink(formats strfmt.Registry) error {
161 |
162 | if err := validate.Required("link", "body", m.Link); err != nil {
163 | return err
164 | }
165 |
166 | if err := validate.FormatOf("link", "body", "uri", m.Link.String(), formats); err != nil {
167 | return err
168 | }
169 |
170 | return nil
171 | }
172 |
173 | func (m *TopicSummary) validateScore(formats strfmt.Registry) error {
174 |
175 | if err := validate.Required("score", "body", m.Score); err != nil {
176 | return err
177 | }
178 |
179 | return nil
180 | }
181 |
182 | var topicSummaryTypeSourcePropEnum []interface{}
183 |
184 | func init() {
185 | var res []string
186 | if err := json.Unmarshal([]byte(`["论坛","自发"]`), &res); err != nil {
187 | panic(err)
188 | }
189 | for _, v := range res {
190 | topicSummaryTypeSourcePropEnum = append(topicSummaryTypeSourcePropEnum, v)
191 | }
192 | }
193 |
194 | const (
195 |
196 | // TopicSummarySourceX论坛 captures enum value "论坛"
197 | TopicSummarySourceX论坛 string = "论坛"
198 |
199 | // TopicSummarySourceX自发 captures enum value "自发"
200 | TopicSummarySourceX自发 string = "自发"
201 | )
202 |
203 | // prop value enum
204 | func (m *TopicSummary) validateSourceEnum(path, location string, value string) error {
205 | if err := validate.Enum(path, location, value, topicSummaryTypeSourcePropEnum); err != nil {
206 | return err
207 | }
208 | return nil
209 | }
210 |
211 | func (m *TopicSummary) validateSource(formats strfmt.Registry) error {
212 |
213 | if err := validate.Required("source", "body", m.Source); err != nil {
214 | return err
215 | }
216 |
217 | // value enum
218 | if err := m.validateSourceEnum("source", "body", *m.Source); err != nil {
219 | return err
220 | }
221 |
222 | return nil
223 | }
224 |
225 | func (m *TopicSummary) validateTitle(formats strfmt.Registry) error {
226 |
227 | if err := validate.Required("title", "body", m.Title); err != nil {
228 | return err
229 | }
230 |
231 | return nil
232 | }
233 |
234 | func (m *TopicSummary) validateUser(formats strfmt.Registry) error {
235 |
236 | if err := validate.Required("user", "body", m.User); err != nil {
237 | return err
238 | }
239 |
240 | if m.User != nil {
241 | if err := m.User.Validate(formats); err != nil {
242 | if ve, ok := err.(*errors.Validation); ok {
243 | return ve.ValidateName("user")
244 | }
245 | return err
246 | }
247 | }
248 |
249 | return nil
250 | }
251 |
252 | // MarshalBinary interface implementation
253 | func (m *TopicSummary) MarshalBinary() ([]byte, error) {
254 | if m == nil {
255 | return nil, nil
256 | }
257 | return swag.WriteJSON(m)
258 | }
259 |
260 | // UnmarshalBinary interface implementation
261 | func (m *TopicSummary) UnmarshalBinary(b []byte) error {
262 | var res TopicSummary
263 | if err := swag.ReadJSON(b, &res); err != nil {
264 | return err
265 | }
266 | *m = res
267 | return nil
268 | }
269 |
--------------------------------------------------------------------------------
/view/swagger/models/topic_summarys.go:
--------------------------------------------------------------------------------
1 | // Code generated by go-swagger; DO NOT EDIT.
2 |
3 | package models
4 |
5 | // This file was generated by the swagger tool.
6 | // Editing this file might prove futile when you re-run the swagger generate command
7 |
8 | import (
9 | "strconv"
10 |
11 | strfmt "github.com/go-openapi/strfmt"
12 |
13 | "github.com/go-openapi/errors"
14 | "github.com/go-openapi/swag"
15 | )
16 |
17 | // TopicSummarys 列表页返回的数据格式
18 | // swagger:model topic_summarys
19 | type TopicSummarys []*TopicSummary
20 |
21 | // Validate validates this topic summarys
22 | func (m TopicSummarys) Validate(formats strfmt.Registry) error {
23 | var res []error
24 |
25 | for i := 0; i < len(m); i++ {
26 | if swag.IsZero(m[i]) { // not required
27 | continue
28 | }
29 |
30 | if m[i] != nil {
31 | if err := m[i].Validate(formats); err != nil {
32 | if ve, ok := err.(*errors.Validation); ok {
33 | return ve.ValidateName(strconv.Itoa(i))
34 | }
35 | return err
36 | }
37 | }
38 |
39 | }
40 |
41 | if len(res) > 0 {
42 | return errors.CompositeValidationError(res...)
43 | }
44 | return nil
45 | }
46 |
--------------------------------------------------------------------------------
/view/swagger/models/user_summary.go:
--------------------------------------------------------------------------------
1 | // Code generated by go-swagger; DO NOT EDIT.
2 |
3 | package models
4 |
5 | // This file was generated by the swagger tool.
6 | // Editing this file might prove futile when you re-run the swagger generate command
7 |
8 | import (
9 | strfmt "github.com/go-openapi/strfmt"
10 |
11 | "github.com/go-openapi/errors"
12 | "github.com/go-openapi/swag"
13 | "github.com/go-openapi/validate"
14 | )
15 |
16 | // UserSummary 用于列表页等用户信息显示
17 | // swagger:model user_summary
18 | type UserSummary struct {
19 |
20 | // 用户唯一id
21 | // Required: true
22 | ID *int64 `json:"id"`
23 |
24 | // 用户显示名称
25 | // Required: true
26 | Name *string `json:"name"`
27 |
28 | // 用户显示小头像
29 | // Required: true
30 | SmallImage *string `json:"small_image"`
31 | }
32 |
33 | // Validate validates this user summary
34 | func (m *UserSummary) Validate(formats strfmt.Registry) error {
35 | var res []error
36 |
37 | if err := m.validateID(formats); err != nil {
38 | res = append(res, err)
39 | }
40 |
41 | if err := m.validateName(formats); err != nil {
42 | res = append(res, err)
43 | }
44 |
45 | if err := m.validateSmallImage(formats); err != nil {
46 | res = append(res, err)
47 | }
48 |
49 | if len(res) > 0 {
50 | return errors.CompositeValidationError(res...)
51 | }
52 | return nil
53 | }
54 |
55 | func (m *UserSummary) validateID(formats strfmt.Registry) error {
56 |
57 | if err := validate.Required("id", "body", m.ID); err != nil {
58 | return err
59 | }
60 |
61 | return nil
62 | }
63 |
64 | func (m *UserSummary) validateName(formats strfmt.Registry) error {
65 |
66 | if err := validate.Required("name", "body", m.Name); err != nil {
67 | return err
68 | }
69 |
70 | return nil
71 | }
72 |
73 | func (m *UserSummary) validateSmallImage(formats strfmt.Registry) error {
74 |
75 | if err := validate.Required("small_image", "body", m.SmallImage); err != nil {
76 | return err
77 | }
78 |
79 | return nil
80 | }
81 |
82 | // MarshalBinary interface implementation
83 | func (m *UserSummary) MarshalBinary() ([]byte, error) {
84 | if m == nil {
85 | return nil, nil
86 | }
87 | return swag.WriteJSON(m)
88 | }
89 |
90 | // UnmarshalBinary interface implementation
91 | func (m *UserSummary) UnmarshalBinary(b []byte) error {
92 | var res UserSummary
93 | if err := swag.ReadJSON(b, &res); err != nil {
94 | return err
95 | }
96 | *m = res
97 | return nil
98 | }
99 |
--------------------------------------------------------------------------------