实现性能
继上一节,咱们实现了根本的web服务。
本节咱们依据语雀凋谢文档 https://www.yuque.com/yuque/developer/api,
新增以下性能
- 语雀文章详情
- 语雀列表
- 语雀搜寻
代码实现
本节残缺代码,参考:https://github.com/golangtips...
减少 servcie 层,并创立以下文件
- service/intf/yuque.go 接口定义
- service/internal/yuque.go 具体外部实现
- service/set.go 服务汇合
定义接口 service/intf/yuque.go
package intfimport ( "context" "time")type IYuQue interface { // GetRepoDocList 获取一个仓库的文档列表 // 文档 https://www.yuque.com/yuque/developer/doc GetRepoDocList(ctx context.Context, request *GetRepoDocListRequest) (*GetRepoDocListResponse, error) // GetRepoDocDetail 获取单篇文档的详细信息 // 文档 https://www.yuque.com/yuque/developer/doc GetRepoDocDetail(ctx context.Context, request *GetRepoDocDetailRequest) (*GetRepoDocDetailResponse, error) // Search 搜寻 // 文档 https://www.yuque.com/yuque/developer/high_level_api Search(ctx context.Context, request *SearchRequest) (*SearchResponse, error)}// GetRepoDocListRequest 获取一个仓库的文档列表type GetRepoDocListRequest struct { Namespace string // Offset int // Limit int // OptionalProperties int // 获取文档浏览数}// GetRepoDocListResponse 获取一个仓库的文档列表type GetRepoDocListResponse struct { Data []Doc `json:"data"`}// GetRepoDocDetailRequest 获取单篇文档的详细信息type GetRepoDocDetailRequest struct { Namespace string Slug string Raw int // raw=1 返回文档最原始的格局}// GetRepoDocDetailResponse 获取单篇文档的详细信息type GetRepoDocDetailResponse struct { Abilities struct { Update bool `json:"update"` Destroy bool `json:"destroy"` } `json:"abilities"` Data DocDetail `json:"data"`}// SearchRequest 搜寻申请type SearchRequest struct { Type string // 资源类型 Offset int // 分页,1、2... Scope int // 搜寻门路 Related bool // 搜寻与我相干的传递 true}// SearchResponse 搜寻后果type SearchResponse struct { // ...}// Doc 文档根本信息,个别用在列表场景// https://www.yuque.com/yuque/developer/docserializertype Doc struct { CreatedAt string `json:"created_at"` ID int64 `json:"id"` Public int64 `json:"public"` Slug string `json:"slug"` Status int64 `json:"status"` Title string `json:"title"` UpdatedAt string `json:"updated_at"`}// DocDetail 文档详细信息// https://www.yuque.com/yuque/developer/docdetailserializertype DocDetail struct { Id int `json:"id"` Slug string `json:"slug"` Title string `json:"title"` BookId int `json:"book_id"` Book struct { Id int `json:"id"` Type string `json:"type"` Slug string `json:"slug"` Name string `json:"name"` UserId int `json:"user_id"` Description string `json:"description"` CreatorId int `json:"creator_id"` Public int `json:"public"` ItemsCount int `json:"items_count"` LikesCount int `json:"likes_count"` WatchesCount int `json:"watches_count"` ContentUpdatedAt time.Time `json:"content_updated_at"` UpdatedAt time.Time `json:"updated_at"` CreatedAt time.Time `json:"created_at"` Namespace string `json:"namespace"` User struct { Id int `json:"id"` Type string `json:"type"` Login string `json:"login"` Name string `json:"name"` Description interface{} `json:"description"` AvatarUrl string `json:"avatar_url"` BooksCount int `json:"books_count"` PublicBooksCount int `json:"public_books_count"` FollowersCount int `json:"followers_count"` FollowingCount int `json:"following_count"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Serializer string `json:"_serializer"` } `json:"user"` Serializer string `json:"_serializer"` } `json:"book"` UserId int `json:"user_id"` Creator struct { Id int `json:"id"` Type string `json:"type"` Login string `json:"login"` Name string `json:"name"` Description interface{} `json:"description"` AvatarUrl string `json:"avatar_url"` BooksCount int `json:"books_count"` PublicBooksCount int `json:"public_books_count"` FollowersCount int `json:"followers_count"` FollowingCount int `json:"following_count"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Serializer string `json:"_serializer"` } `json:"creator"` Format string `json:"format"` Body string `json:"body"` BodyDraft string `json:"body_draft"` BodyHtml string `json:"body_html"` BodyLake string `json:"body_lake"` BodyDraftLake string `json:"body_draft_lake"` Public int `json:"public"` Status int `json:"status"` ViewStatus int `json:"view_status"` ReadStatus int `json:"read_status"` LikesCount int `json:"likes_count"` CommentsCount int `json:"comments_count"` ContentUpdatedAt time.Time `json:"content_updated_at"` DeletedAt interface{} `json:"deleted_at"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` PublishedAt time.Time `json:"published_at"` FirstPublishedAt time.Time `json:"first_published_at"` WordCount int `json:"word_count"` Cover interface{} `json:"cover"` Description string `json:"description"` CustomDescription interface{} `json:"custom_description"` Hits int `json:"hits"` Serializer string `json:"_serializer"`}
接口实现 service/intf/yuque.go
package internalimport ( "context" "encoding/json" "fmt" "io" "log" "net/http" "strconv" "time" "github.com/golangtips/yuque/service/intf")var _ intf.IYuQue = (*YuQue)(nil)type YuQue struct { UserAgent string //利用名称 baseURL string token string client *http.Client}func NewYuQue(baseURL, token, userAgent string) *YuQue { client := &http.Client{ Timeout: 10 * time.Second, } return &YuQue{ UserAgent: userAgent, baseURL: baseURL, token: token, client: client, }}func (y *YuQue) GetRepoDocList(ctx context.Context, request *intf.GetRepoDocListRequest) (*intf.GetRepoDocListResponse, error) { url := fmt.Sprintf("%s/repos/%s/docs", y.baseURL, request.Namespace) req := y.buildHTTPRequest("GET", url, nil) q := req.URL.Query() if request.Offset > 0 { q.Add("offset", strconv.Itoa(request.Offset)) } if request.Limit > 0 { q.Add("limit", strconv.Itoa(request.Limit)) } req.URL.RawQuery = q.Encode() resp, err := y.client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("ioutil: %w", err) } var response intf.GetRepoDocListResponse if err = json.Unmarshal(body, &response); err != nil { return nil, err } return &response, nil}func (y *YuQue) GetRepoDocDetail(_ context.Context, request *intf.GetRepoDocDetailRequest) (*intf.GetRepoDocDetailResponse, error) { url := fmt.Sprintf("%s/repos/%s/docs/%s", y.baseURL, request.Namespace, request.Slug) req := y.buildHTTPRequest("GET", url, nil) resp, err := y.client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("ioutil: %w", err) } log.Println(string(body)) var detail intf.GetRepoDocDetailResponse if err = json.Unmarshal(body, &detail); err != nil { return nil, err } return &detail, nil}func (y *YuQue) Search(ctx context.Context, request *intf.SearchRequest) (*intf.SearchResponse, error) { return &intf.SearchResponse{ // }, nil}// buildHTTPRequest 辅助函数func (y *YuQue) buildHTTPRequest(method, url string, body io.Reader) *http.Request { req, _ := http.NewRequest(method, url, body) req.Header.Add("User-Agent", y.UserAgent) req.Header.Add("X-Auth-Token", y.token) return req}
增加到服务汇合 service/set.go
package serviceimport ( "github.com/golangtips/yuque/config" "github.com/golangtips/yuque/service/internal" "github.com/golangtips/yuque/service/intf")type Set struct { YuQue intf.IYuQue}func NewSet(toml *config.Toml) (*Set, error) { var yueque intf.IYuQue { c := toml.YuQue yueque = internal.NewYuQue(c.BaseURL, c.Token, c.UserAgent) } return &Set{ YuQue: yueque, }, nil}