ai_scheduler/internal/tools/public/weather.go

346 lines
9.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package public
import (
"ai_scheduler/internal/config"
"ai_scheduler/internal/entitys"
"ai_scheduler/internal/pkg/l_request"
"log"
"strconv"
"context"
"encoding/json"
"fmt"
"time"
)
// WeatherTool 天气查询工具
type WeatherTool struct {
mockData bool
config config.ToolConfig
}
// NewWeatherTool 创建天气工具
func NewWeatherTool(config config.ToolConfig) *WeatherTool {
return &WeatherTool{config: config}
}
// Name 返回工具名称
func (w *WeatherTool) Name() string {
return "get_weather"
}
// Description 返回工具描述
func (w *WeatherTool) Description() string {
return "获取指定城市的天气信息"
}
// Definition 返回工具定义
func (w *WeatherTool) Definition() entitys.ToolDefinition {
return entitys.ToolDefinition{
Type: "function",
Function: entitys.FunctionDef{
Name: w.Name(),
Description: w.Description(),
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"city": map[string]interface{}{
"type": "string",
"description": "城市名称,如:北京、上海、广州",
},
"unit": map[string]interface{}{
"type": "string",
"description": "温度单位celsius(摄氏度)或fahrenheit(华氏度)",
"enum": []string{"celsius", "fahrenheit"},
"default": "celsius",
},
"extensions": map[string]interface{}{
"type": "string",
"description": "扩展参数base/all base:返回实况天气 all:返回预报天气",
"enum": []string{"base", "all"},
"default": "base",
},
},
"required": []string{"city"},
},
},
}
}
// WeatherRequest 天气请求参数
type WeatherRequest struct {
City string `json:"city"`
Extensions string `json:"extensions"` // 扩展参数base/all base:返回实况天气 all:返回预报天气
Unit string `json:"unit,omitempty"`
}
// WeatherResponse 天气响应
type WeatherResponse struct {
City string `json:"city"`
Unit string `json:"unit"`
Timestamp string `json:"timestamp"`
LiveWeather *LiveWeather `json:"live_weather,omitempty"` // 实时天气
Forecasts []ForecastWeather `json:"forecasts,omitempty"` // 预报天气
}
// ForecastWeather 预报天气
type ForecastWeather struct {
Date string `json:"date"`
Week string `json:"week"`
DayWeather string `json:"day_weather"`
NightWeather string `json:"night_weather"`
DayTemp float64 `json:"day_temp"`
NightTemp float64 `json:"night_temp"`
DayWind string `json:"day_wind"`
NightWind string `json:"night_wind"`
DayWindPower string `json:"day_wind_power"`
NightWindPower string `json:"night_wind_power"`
}
// LiveWeather 实时天气
type LiveWeather struct {
Temperature float64 `json:"temperature"`
Condition string `json:"condition"`
Humidity int `json:"humidity"`
WindSpeed float64 `json:"wind_speed"`
WindDirection string `json:"wind_direction"`
}
// Execute 执行天气查询
func (w *WeatherTool) Execute(ctx context.Context, requireData *entitys.RequireData) error {
var req WeatherRequest
if err := json.Unmarshal([]byte(requireData.Match.Parameters), &req); err != nil {
return fmt.Errorf("invalid weather request: %w", err)
}
if req.City == "" {
return fmt.Errorf("city is required")
}
if req.Unit == "" {
req.Unit = "celsius"
}
// 设置默认获取实时天气信息
if req.Extensions == "" {
req.Extensions = "base"
}
// 这里可以集成真实的天气API
responseMsg, err := w.getRealWeather(req)
if err != nil {
return fmt.Errorf("failed to get real weather: %w", err)
}
// 根据 extensions 参数返回不同的天气信息
if req.Extensions == "base" {
entitys.ResText(requireData.Ch, "", fmt.Sprintf("%s实时天气%s温度%.1f℃,湿度:%d%%,风速:%.1fkm/h风向%s",
req.City,
responseMsg.LiveWeather.Condition,
responseMsg.LiveWeather.Temperature,
responseMsg.LiveWeather.Humidity,
responseMsg.LiveWeather.WindSpeed,
responseMsg.LiveWeather.WindDirection))
} else {
rspStr := fmt.Sprintf("%s天气预报\n", req.City)
for _, forecast := range responseMsg.Forecasts {
rspStr += fmt.Sprintf("%s 温度:%.1f℃/%.1f℃ 风力:%s %s\n",
forecast.Date, forecast.DayTemp, forecast.NightTemp, forecast.DayWind, forecast.NightWind)
}
entitys.ResText(requireData.Ch, "", rspStr)
}
return nil
}
//// getMockWeather 获取模拟天气数据
//func (w *WeatherTool) getMockWeather(city, unit string) *WeatherResponse {
// rand.Seed(time.Now().UnixNano())
//
// // 模拟不同城市的基础温度
// baseTemp := map[string]float64{
// "北京": 15.0,
// "上海": 18.0,
// "广州": 25.0,
// "深圳": 26.0,
// "杭州": 17.0,
// "成都": 16.0,
// }
//
// temp := baseTemp[city]
// if temp == 0 {
// temp = 20.0 // 默认温度
// }
//
// // 添加随机变化
// temp += (rand.Float64() - 0.5) * 10
//
// // 转换温度单位
// if unit == "fahrenheit" {
// temp = temp*9/5 + 32
// }
//
// conditions := []string{"晴朗", "多云", "阴天", "小雨", "中雨"}
// condition := conditions[rand.Intn(len(conditions))]
//
// return &WeatherResponse{
// City: city,
// Temperature: float64(int(temp*10)) / 10, // 保留一位小数
// Unit: unit,
// Condition: condition,
// Humidity: rand.Intn(40) + 40, // 40-80%
// WindSpeed: float64(rand.Intn(20)) + 1.0,
// Timestamp: time.Now().Format("2006-01-02 15:04:05"),
// }
//}
// getRealWeather 调用高德天气API
func (w *WeatherTool) getRealWeather(request WeatherRequest) (*WeatherResponse, error) {
// 构建请求URL
req := l_request.Request{
Url: w.config.BaseURL,
Headers: map[string]string{},
Params: map[string]string{
"city": request.City, // 城市名称
"key": w.config.APIKey, // API密钥
// extensions: 基础天气数据 可选值base/all base:返回实况天气 all:返回预报天气
"extensions": request.Extensions, // 基础天气数据
"output": "JSON", // JSON格式返回
},
Method: "GET",
}
res, err := req.Send()
if err != nil {
return nil, err
}
// 解析API响应
var apiResp struct {
Status string `json:"status"`
Count string `json:"count"`
Info string `json:"info"`
Infocode string `json:"infocode"`
// 预报天气信息数据
Forecasts []struct {
City string `json:"city"`
Adcode string `json:"adcode"`
Province string `json:"province"`
Reporttime string `json:"reporttime"`
Casts []struct {
Date string `json:"date"`
Week string `json:"week"`
Dayweather string `json:"dayweather"`
Nightweather string `json:"nightweather"`
Daytemp string `json:"daytemp"`
Nighttemp string `json:"nighttemp"`
Daywind string `json:"daywind"`
Nightwind string `json:"nightwind"`
Daypower string `json:"daypower"`
Nightpower string `json:"nightpower"`
DaytempFloat string `json:"daytemp_float"`
NighttempFloat string `json:"nighttemp_float"`
} `json:"casts"`
} `json:"forecasts"`
// 实况天气信息数据
Lives []struct {
Province string `json:"province"`
City string `json:"city"`
Adcode string `json:"adcode"`
Weather string `json:"weather"`
Temperature string `json:"temperature"`
Winddirection string `json:"winddirection"`
Windpower string `json:"windpower"`
Humidity string `json:"humidity"`
Reporttime string `json:"reporttime"`
TemperatureFloat string `json:"temperature_float"`
HumidityFloat string `json:"humidity_float"`
} `json:"lives"`
}
log.Printf("weather API response: %s", string(res.Content))
if err = json.Unmarshal(res.Content, &apiResp); err != nil {
return nil, fmt.Errorf("parse weather API response failed: %w", err)
}
// 检查API返回状态
if apiResp.Status != "1" {
return nil, fmt.Errorf("weather API returned error: %s, info: %s", apiResp.Status, apiResp.Info)
}
// 获取城市名称
cityName := ""
if len(apiResp.Lives) > 0 {
cityName = apiResp.Lives[0].City
} else if len(apiResp.Forecasts) > 0 {
cityName = apiResp.Forecasts[0].City
} else {
return nil, fmt.Errorf("no weather data found")
}
// 构建响应
response := &WeatherResponse{
City: cityName,
Unit: request.Unit,
Timestamp: time.Now().Format("2006-01-02 15:04:05"),
}
// 处理实时天气
if len(apiResp.Lives) > 0 {
liveData := apiResp.Lives[0]
// 转换温度
temp, _ := strconv.ParseFloat(liveData.Temperature, 64)
if request.Unit == "fahrenheit" {
temp = temp*9/5 + 32
}
// 转换湿度和风速
humidity, _ := strconv.Atoi(liveData.Humidity)
windSpeed, _ := strconv.ParseFloat(liveData.Windpower, 64)
response.LiveWeather = &LiveWeather{
Temperature: temp,
Condition: liveData.Weather,
Humidity: humidity,
WindSpeed: windSpeed,
WindDirection: liveData.Winddirection,
}
}
// 处理预报天气
if len(apiResp.Forecasts) > 0 && len(apiResp.Forecasts[0].Casts) > 0 {
response.Forecasts = make([]ForecastWeather, 0, len(apiResp.Forecasts[0].Casts))
for _, cast := range apiResp.Forecasts[0].Casts {
// 转换温度
dayTemp, _ := strconv.ParseFloat(cast.Daytemp, 64)
nightTemp, _ := strconv.ParseFloat(cast.Nighttemp, 64)
if request.Unit == "fahrenheit" {
dayTemp = dayTemp*9/5 + 32
nightTemp = nightTemp*9/5 + 32
}
forecast := ForecastWeather{
Date: cast.Date,
Week: cast.Week,
DayWeather: cast.Dayweather,
NightWeather: cast.Nightweather,
DayTemp: dayTemp,
NightTemp: nightTemp,
DayWind: cast.Daywind,
NightWind: cast.Nightwind,
DayWindPower: cast.Daypower,
NightWindPower: cast.Nightpower,
}
response.Forecasts = append(response.Forecasts, forecast)
}
}
return response, nil
}