feat(server-go): 和风天气接入与高发病天气预警规则(#12)
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const qweatherAPIBase = "https://devapi.qweather.com"
|
||||
|
||||
// WeatherNow 实时天气
|
||||
type WeatherNow struct {
|
||||
Temp float64 `json:"temp"`
|
||||
Humidity float64 `json:"humidity"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
// DailyForecast 逐日预报
|
||||
type DailyForecast struct {
|
||||
Date string `json:"date"`
|
||||
TextDay string `json:"textDay"`
|
||||
TextNight string `json:"textNight"`
|
||||
TempMax float64 `json:"tempMax"`
|
||||
TempMin float64 `json:"tempMin"`
|
||||
Humidity float64 `json:"humidity"`
|
||||
}
|
||||
|
||||
// WeatherService 和风天气客户端(未配置时 Configured()=false,调用返回明确错误)
|
||||
type WeatherService struct {
|
||||
key string
|
||||
location string
|
||||
baseURL string
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// NewWeatherService 创建和风天气客户端
|
||||
func NewWeatherService(key, location string) *WeatherService {
|
||||
return &WeatherService{
|
||||
key: key,
|
||||
location: location,
|
||||
baseURL: qweatherAPIBase,
|
||||
client: &http.Client{Timeout: 15 * time.Second},
|
||||
}
|
||||
}
|
||||
|
||||
// Configured 是否已配置 API Key 与位置
|
||||
func (s *WeatherService) Configured() bool {
|
||||
return s.key != "" && s.location != ""
|
||||
}
|
||||
|
||||
// FetchNow 实时天气
|
||||
func (s *WeatherService) FetchNow(ctx context.Context) (*WeatherNow, error) {
|
||||
var out struct {
|
||||
Code string `json:"code"`
|
||||
Now struct {
|
||||
Temp string `json:"temp"`
|
||||
Text string `json:"text"`
|
||||
Humidity string `json:"humidity"`
|
||||
} `json:"now"`
|
||||
}
|
||||
if err := s.get(ctx, "/v7/weather/now", &out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
temp, _ := strconv.ParseFloat(out.Now.Temp, 64)
|
||||
hum, _ := strconv.ParseFloat(out.Now.Humidity, 64)
|
||||
return &WeatherNow{Temp: temp, Humidity: hum, Text: out.Now.Text}, nil
|
||||
}
|
||||
|
||||
// FetchDaily 未来 3 天预报
|
||||
func (s *WeatherService) FetchDaily(ctx context.Context) ([]DailyForecast, error) {
|
||||
var out struct {
|
||||
Code string `json:"code"`
|
||||
Daily []struct {
|
||||
FxDate string `json:"fxDate"`
|
||||
TextDay string `json:"textDay"`
|
||||
TextNight string `json:"textNight"`
|
||||
TempMax string `json:"tempMax"`
|
||||
TempMin string `json:"tempMin"`
|
||||
Humidity string `json:"humidity"`
|
||||
} `json:"daily"`
|
||||
}
|
||||
if err := s.get(ctx, "/v7/weather/3d", &out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list := make([]DailyForecast, 0, len(out.Daily))
|
||||
for _, d := range out.Daily {
|
||||
max, _ := strconv.ParseFloat(d.TempMax, 64)
|
||||
min, _ := strconv.ParseFloat(d.TempMin, 64)
|
||||
hum, _ := strconv.ParseFloat(d.Humidity, 64)
|
||||
list = append(list, DailyForecast{
|
||||
Date: d.FxDate, TextDay: d.TextDay, TextNight: d.TextNight,
|
||||
TempMax: max, TempMin: min, Humidity: hum,
|
||||
})
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// get 发起 GET 并校验 code=200
|
||||
func (s *WeatherService) get(ctx context.Context, path string, out any) error {
|
||||
if !s.Configured() {
|
||||
return fmt.Errorf("天气服务未配置(QWEATHER_API_KEY/QWEATHER_LOCATION)")
|
||||
}
|
||||
u := s.baseURL + path + "?location=" + url.QueryEscape(s.location) + "&key=" + url.QueryEscape(s.key)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := s.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(body, out); err != nil {
|
||||
return err
|
||||
}
|
||||
var head struct {
|
||||
Code string `json:"code"`
|
||||
}
|
||||
_ = json.Unmarshal(body, &head)
|
||||
if head.Code != "200" {
|
||||
return fmt.Errorf("和风天气返回错误 code=%s", head.Code)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user