Golang 语雀内容系统(2) - 增加服务层语雀SDK

实现功能

继上一节,我们完成了基本的web服务。
本节我们根据语雀开放文档 https://www.yuque.com/yuque/developer/api
新增以下功能

  • 语雀文章详情
  • 语雀列表
  • 语雀搜索

代码实现

增加 servcie 层,并创建以下文件

  • service/intf/yuque.go 接口定义
  • service/internal/yuque.go 具体内部实现
  • service/set.go 服务集合
  1. 定义接口 service/intf/yuque.go

    package intf
    
    import (
     "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/docserializer
    type 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/docdetailserializer
    type 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"`
    }
    
  2. 接口实现 service/intf/yuque.go

    package internal
    
    import (
     "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
    }
    
  3. 添加到服务集合 service/set.go

    package service
    
    import (
     "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
    }
    

你可能感兴趣的:(goblog)