package fwh_pro import ( "bytes" "encoding/base64" "encoding/json" "fmt" "io" "net/http" "net/url" "strconv" "strings" "time" ) var loginInfo LoginData = LoginData{ UserId: 0, Token: "", } type LoginPayload struct { LoginName string `json:"loginName"` Password string `json:"password"` Type int `json:"type"` Lang string `json:"lang"` } type LoginData struct { UserId uint `json:"userId"` Token string `json:"token"` } type LoginResponse struct { Code int `json:"code"` Msg string `json:"msg"` Data LoginData `json:"data"` Success bool `json:"success"` } type SiteInfoListPayload struct { Search *string `json:"search,omitempty"` // 只能搜索单条SN Serials *string `json:"serials,omitempty"` // 可搜索多条SN,SN之间以‘,’隔开 CountryId *int `json:"countryId,omitempty"` ProvinceId *int `json:"provinceId,omitempty"` CityId *int `json:"cityId,omitempty"` NetworkStatus *int `json:"networkStatus,omitempty"` DeviceStatus *int `json:"deviceStatus,omitempty"` OnOffStatus *int `json:"onOffStatus,omitempty"` PageSize uint `json:"pageSize"` PageNum uint `json:"pageNum"` Lang string `json:"lang"` } type SiteModeInfoPayload struct { GatewayId string `json:"gatewayId"` ShowType int `json:"showType"` Lang string `json:"lang"` } func Login() error { // 要发送的 JSON payload := LoginPayload{ LoginName: "FMH001", Password: "cfd9d803fafcf2c7a832bfba5d039512", Type: 1, Lang: "ZH_CN", } jsonData, _ := json.Marshal(payload) // 创建请求 req, err := http.NewRequest( http.MethodPost, "https://oss.franklinwh.com/api-user/user/login", bytes.NewBuffer(jsonData)) if err != nil { fmt.Println(err.Error()) return err } // 设置 Content-Type 和 Authorization req.Header.Set("Content-Type", "application/json") // 发送请求 client := &http.Client{Timeout: 5 * time.Second} resp, err := client.Do(req) if err != nil { fmt.Println(err.Error()) return err } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) // fmt.Printf("Status: %s\nBody: %s", resp.Status, body) var loginRes LoginResponse // 反序列化(unmarshal) jsonErr := json.Unmarshal(body, &loginRes) if jsonErr != nil { return jsonErr } loginInfo.UserId = loginRes.Data.UserId loginInfo.Token = loginRes.Data.Token return nil } func isTokenExpired(token string) (bool, error) { parts := strings.Split(token, ".") if len(parts) != 3 { return false, fmt.Errorf("token 格式错误") } payloadSegment := parts[1] // JWT 使用 base64 URL encoding payloadBytes, err := base64.RawURLEncoding.DecodeString(payloadSegment) if err != nil { return false, err } var payload map[string]interface{} if err := json.Unmarshal(payloadBytes, &payload); err != nil { return false, err } expFloat, ok := payload["exp"].(float64) if !ok { return false, fmt.Errorf("token payload 中没有 exp") } expTime := time.Unix(int64(expFloat), 0) return time.Now().After(expTime), nil } func getLoginToken() string { // 判断是否已经有token if loginInfo.Token != "" { // 判断token是否过期 expired, err := isTokenExpired(loginInfo.Token) if err != nil { fmt.Println("loginInfo.Token 解析出错:", err) return "" } if expired { fmt.Println("Token 已过期") } else { // fmt.Println("Token 有效") return loginInfo.Token } } // 重新登录 Login() return loginInfo.Token } // 清空token func ClearToken() { loginInfo.UserId = 0 loginInfo.Token = "" } func QuerySiteInfoList(sn []string, pageSize uint, pageNum uint) ([]byte, error) { snList := strings.Join(sn, ",") token := getLoginToken() // 要发送的 JSON payload := SiteInfoListPayload{ Serials: &snList, PageSize: pageSize, PageNum: pageNum, Lang: "ZH_CN", } // 要发送的表单数据 form := url.Values{} form.Set("serials", *payload.Serials) // *string 转 string form.Set("pageSize", fmt.Sprintf("%d", payload.PageSize)) form.Set("pageNum", fmt.Sprintf("%d", payload.PageNum)) form.Set("lang", payload.Lang) // 创建请求 req, err := http.NewRequest( http.MethodPost, "https://energy.franklinwh.com/hes-gateway/manage/selectIotFirmwareGatewayList", strings.NewReader(form.Encode())) if err != nil { fmt.Println(err.Error()) return nil, err } // 设置 Content-Type 和 Authorization req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") req.Header.Set("Logintoken", token) // 发送请求 client := &http.Client{Timeout: 5 * time.Second} resp, err := client.Do(req) if err != nil { fmt.Println(err.Error()) return nil, err } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) // fmt.Printf("Status: %s\nBody: %s\n", resp.Status, body) return body, nil } func QuerySiteModeInfo(sn string) ([]byte, error) { token := getLoginToken() // 要发送的 JSON payload := SiteModeInfoPayload{ GatewayId: sn, ShowType: 1, Lang: "ZH_CN", } // 要发送的表单数据 form := url.Values{} form.Set("gatewayId", payload.GatewayId) form.Set("showType", strconv.Itoa(payload.ShowType)) form.Set("lang", payload.Lang) // 创建请求 req, err := http.NewRequest( http.MethodPost, "https://energy.franklinwh.com/hes-gateway/manage/tou/getGatewayTouListV2", strings.NewReader(form.Encode())) if err != nil { fmt.Println(err.Error()) return nil, err } // 设置 Content-Type 和 Authorization req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") req.Header.Set("Logintoken", token) // fmt.Println(req.Header) // 发送请求 client := &http.Client{Timeout: 5 * time.Second} resp, err := client.Do(req) if err != nil { fmt.Println(err.Error()) return nil, err } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) // fmt.Printf("Status: %s\nBody: %s\n", resp.Status, body) return body, nil } func QuerySitePowerFlow(sn string, day time.Time) ([]byte, error) { baseURL := "https://energy.franklinwh.com/api-energy/power/getFhpPowerByDayOld" token := getLoginToken() // 构造 query 参数 params := url.Values{} params.Add("gatewayId", sn) params.Add("dayTime", day.Format(time.DateOnly)) // 会自动 URL 编码 // 拼接到 URL finalURL := baseURL + "?" + params.Encode() // 创建请求 req, err := http.NewRequest( http.MethodGet, finalURL, nil) if err != nil { fmt.Println(err.Error()) return nil, err } // 设置 Content-Type 和 Authorization req.Header.Set("Content-Type", "application/json") req.Header.Set("Logintoken", token) // fmt.Println(req.Header) // 发送请求 client := &http.Client{Timeout: 5 * time.Second} resp, err := client.Do(req) if err != nil { fmt.Println(err.Error()) return nil, err } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) // fmt.Printf("Status: %s\nBody: %s\n", resp.Status, body) return body, nil }