346 lines
9.9 KiB
Go
346 lines
9.9 KiB
Go
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
|
||
|
||
}
|