通過cobra開發天氣查詢小工具

Remember發表於2020-10-11

介紹 cobra

cobra 是一個 Golang 包,它提供了簡單的介面來建立命令列程式。同時它也是一個應用程式,用來生成應用框架,從而開發以 cobra 為基礎的應用。在 GitHub 上,有更多關於 cobra 的介紹。網上也有關於此專案的一些文章,只是 10 篇文章到最後可能 9 篇都是重複的,僅此而已。

說明

為了不增加文章的重複率,也為了達到學習的效果,通過此專案來實現一些小功能。最終的效果是通過命令列輸入城市名或者城市碼獲取當前城市的溫度情況。

實現

入口檔案的目的只有一個初始化 cobra

func main() {
    _ = cmd.Execute()
}
package cmd

import (
    "github.com/spf13/cobra"
)

var (
    weatherCmd = &cobra.Command{
        Use: "weather",
    }
)

func Execute() error {
    return weatherCmd.Execute()
}

然後在其他的兩個檔案中,分別定義了此 cmd 的兩個子命令。分別表示通過城市名稱 NameCmd 和通過城市碼 CodeCmd來查詢天氣情況。

通過城市名稱

package cmd

import (
    "errors"
    "fmt"
    "os"

    "github.com/spf13/cobra"
    "github.com/wuqinqiang/go-weather/server"
    "github.com/wuqinqiang/go-weather/tools"
)

func init() {
    NameCmd.PersistentFlags().StringP("name", "n", "", "input city name")
    weatherCmd.AddCommand(NameCmd)
}

var NameCmd = &cobra.Command{
    Use:   "name",
    Short: "check city weather by city name",
    Args: func(cmd *cobra.Command, args []string) error {
        name, err := cmd.Flags().GetString("name")
        if err != nil {
            return errors.New("please input city name")
        }
        if len(name) == 0 {
            return errors.New("請攜帶引數-n 或者 --name")
        }
        return nil
    },
    Run: func(cmd *cobra.Command, args []string) {
        name, _ := cmd.Flags().GetString("name")
        code := tools.CityMap[name]
        if code == 0 {
            fmt.Println("這個城市我不想查")
            os.Exit(1)
        }
        info, err := server.GetWeather(code)
        if err != nil {
            fmt.Println(err.Error())
            os.Exit(1)
        }
        fmt.Println("查詢的天氣是:", info)
    },
}

通過城市碼

package cmd

import (
    "errors"
    "fmt"
    "os"

    "github.com/spf13/cobra"
    "github.com/wuqinqiang/go-weather/server"
)

func init() {
    codeCmd.PersistentFlags().IntP("code", "c", 0, "城市碼必須是6位的整數")
    weatherCmd.AddCommand(codeCmd)
}

var codeCmd = &cobra.Command{
    Use:   "code",
    Short: "check city weather by city code",
    Args: func(cmd *cobra.Command, args []string) error {
        code, err := cmd.Flags().GetInt("code")
        if err != nil {
            return errors.New("請輸入城市碼")
        }
        if code == 0 {
            return errors.New("請攜帶引數-c 或者 --code")
        }
        return nil
    },
    Run: func(cmd *cobra.Command, args []string) {
        code, _ := cmd.Flags().GetInt("code")
        info, err := server.GetWeather(code)
        if err != nil {
            fmt.Println(err.Error())
            os.Exit(1)
        }
        fmt.Println("查詢的天氣是:", info)
    },
}

cobra.Command 裡面的 Args 主要做一些引數驗證,Run 就是實際工作功能,也是核心部分,大部分命令只會實現這一點。

至於獲取天氣,你可以看到 server.GetWeather 其實就是對接高德的 api 介面,傳送了一個 http 請求罷了。


const (
     Key = "xx" //高德key
    Uri = "https://restapi.amap.com/v3/weather/weatherInfo" //api地址
)


//響應天氣資料
func GetWeather(code int) (map[string]interface{}, error) {
    info, err := GetWeatherRequest(code)
    if err != nil {
        return nil, err
    }
    infoMap := make(map[string]interface{})
    infoMap["城市:"] = info.Lives[0].City
    infoMap["天氣現象:"] = info.Lives[0].Weather
    infoMap["實時氣溫:"] = info.Lives[0].Temperature
    infoMap["資料釋出:"] = info.Lives[0].Reporttime
    return infoMap, nil
}

//請求介面
func GetWeatherRequest(code int) (entity.ResponseInfo, error) {
    info := entity.ResponseInfo{}
    client := &http.Client{Timeout: 2 * time.Second}
    url := fmt.Sprintf(Uri+"?key=%s&city=%d", Key, code)
    resp, err := client.Get(url)
    if err != nil {
        fmt.Println("查詢錯誤:", err)
        return info, errors.New("查詢失敗")
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("read body err:", err)
        return info, errors.New("查詢失敗")
    }
    if err := json.Unmarshal(body, &info); err != nil {
        fmt.Println("unmarshal response err:", err)
        return info, errors.New("查詢失敗,請輸入正確的城市碼")
    }

    if info.Status != "1" {
        return info, errors.New("api 金鑰key錯誤,請檢查")
    }
    return info, nil
}

這一塊沒咋麼設計,使得獲取天氣的資訊高度依賴於高德的介面,假設我們現在用其他的方式來獲取天氣,那麼就需要大調整當前的程式碼。這一塊理應抽象化。
最後,整個專案的結構如下:

專案的地址在:github.com/wuqinqiang/go-weather ,感興趣可以看下

本作品採用《CC 協議》,轉載必須註明作者和本文連結
吳親庫裡

相關文章