| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- package main
- import (
- "bytes"
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "os"
- "time"
- )
- // 运行方式
- // export FWH_TOKEN="你的logintoken值"
- // cd service/test/tou
- // go run main.go
- // ===== 配置 =====
- const (
- baseURL = "https://test.franklinwh.com"
- lang = "ZH_CN"
- )
- // ===== 数据结构 =====
- type APIResponse struct {
- Code int `json:"code"`
- Message string `json:"message"`
- Result json.RawMessage `json:"result"`
- Success bool `json:"success"`
- }
- type Province struct {
- ID int `json:"id"`
- NameEn string `json:"nameEn"`
- NameZh string `json:"nameZh"`
- }
- type TemplateData struct {
- Template TemplateInfo `json:"template"`
- StrategyList []StrategyInfo `json:"strategyList"`
- }
- type TemplateInfo struct {
- ID int `json:"id"`
- ElectricCompany string `json:"electricCompany"`
- EleCompanyFullName string `json:"eleCompanyFullName"`
- DerSchdule string `json:"derSchdule"`
- GridType string `json:"gridType"`
- Name string `json:"name"`
- ElectricityType int `json:"electricityType"`
- CountryId int `json:"countryId"`
- ProvinceId int `json:"provinceId"`
- ProvinceEn string `json:"provinceEn"`
- Env int `json:"env"`
- MookRunCount int `json:"mookRunCount"`
- Priority int `json:"priority"`
- }
- type StrategyInfo struct {
- SeasonName string `json:"seasonName"`
- Month string `json:"month"`
- DayName string `json:"dayName"`
- DayType int `json:"dayType"`
- CcociateSn string `json:"ccociateSn"`
- CcociateType int `json:"ccociateType"`
- EleticRatePeak *float64 `json:"eleticRatePeak"`
- EleticRateShoulder *float64 `json:"eleticRateShoulder"`
- EleticRateValley *float64 `json:"eleticRateValley"`
- EleticRateSharp *float64 `json:"eleticRateSharp"`
- EleticRateSuperOffPeak *float64 `json:"eleticRateSuperOffPeak"`
- EleticRateGridFee *float64 `json:"eleticRateGridFee"`
- EleticSellPeak *float64 `json:"eleticSellPeak"`
- EleticSellShoulder *float64 `json:"eleticSellShoulder"`
- EleticSellValley *float64 `json:"eleticSellValley"`
- EleticSellSharp *float64 `json:"eleticSellSharp"`
- EleticSellSuperOffPeak *float64 `json:"eleticSellSuperOffPeak"`
- DetailVoList []DetailInfo `json:"detailVoList"`
- }
- type DetailInfo struct {
- StartHourTime string `json:"startHourTime"`
- EndHourTime string `json:"endHourTime"`
- WaveType int `json:"waveType"`
- Name string `json:"name"`
- BriefDescribe string `json:"briefDescribe"`
- SolarPriority string `json:"solarPriority"`
- GridDischargeMax *float64 `json:"gridDischargeMax"`
- GridChargeMax *float64 `json:"gridChargeMax"`
- ChargePower *float64 `json:"chargePower"`
- DischargePower *float64 `json:"dischargePower"`
- ChargeMax *float64 `json:"chargeMax"`
- DischargeMax *float64 `json:"dischargeMax"`
- GcaoMax *float64 `json:"gcaoMax"`
- LoadPriority string `json:"loadPriority"`
- GridFeedMax *float64 `json:"gridFeedMax"`
- GridMax *float64 `json:"gridMax"`
- MinDischargeSoc *int `json:"minDischargeSoc"`
- MaxChargeSoc *int `json:"maxChargeSoc"`
- HeatEnable *int `json:"heatEnable"`
- PowerOffApower string `json:"powerOffApower"`
- RampTime *int `json:"rampTime"`
- OffGrid *int `json:"offGrid"`
- SolarCutoff *int `json:"solarCutoff"`
- UseModeFlag int `json:"useModeFlag"`
- DispatchId int `json:"dispatchId"`
- BuyRate *float64 `json:"buyRate"`
- SellRate *float64 `json:"sellRate"`
- }
- // 创建模版的请求体
- type CreateTemplatePayload struct {
- Template CreateTemplateInfo `json:"template"`
- StrategyList []StrategyInfo `json:"strategyList"`
- Lang string `json:"lang"`
- }
- type CreateTemplateInfo struct {
- CountryId int `json:"countryId"`
- ProvinceId int `json:"provinceId"`
- DerSchdule string `json:"derSchdule"`
- EleCompanyFullName string `json:"eleCompanyFullName"`
- ElectricCompany string `json:"electricCompany"`
- GridType string `json:"gridType"`
- ElectricityType int `json:"electricityType"`
- Name string `json:"name"`
- MookRunCount string `json:"mookRunCount"`
- Priority string `json:"priority"`
- Env string `json:"env"`
- }
- // ===== HAR 数据:三个模版的策略数据 =====
- func getTemplateData() []TemplateData {
- // 模版 4477 - ZEROHERO (GloBird Energy, Queensland)
- int90 := 90
- float5 := 5.0
- float0_1 := 0.1
- float10 := 10.0
- float0 := 0.0
- int1 := 1
- int0 := 0
- template4477 := TemplateData{
- Template: TemplateInfo{
- ID: 4477, ElectricCompany: "GloBird Energy", EleCompanyFullName: "GloBird Energy",
- DerSchdule: "ZEROHERO", GridType: "ZEROHERO", Name: "ZEROHERO",
- ElectricityType: 1, CountryId: 3, ProvinceId: 88, ProvinceEn: "Queensland",
- Env: 0, MookRunCount: 0, Priority: 0,
- },
- StrategyList: []StrategyInfo{
- {
- SeasonName: "All months", Month: "1,2,3,4,5,6,7,8,9,10,11,12",
- DayName: "Every day", DayType: 3, CcociateSn: "", CcociateType: 1,
- DetailVoList: []DetailInfo{
- {StartHourTime: "00:00", EndHourTime: "11:00", WaveType: 1, Name: "mid-peak", BriefDescribe: "Self-consumption", SolarPriority: "1,3,2", LoadPriority: "1,3,2", SolarCutoff: &int0, UseModeFlag: 0, DispatchId: 6, PowerOffApower: ""},
- {StartHourTime: "11:00", EndHourTime: "14:00", WaveType: 0, Name: "off-peak", BriefDescribe: "aPower charger from solar", SolarPriority: "1,2,3", GridChargeMax: &float5, LoadPriority: "1,2,0", SolarCutoff: &int0, UseModeFlag: 0, DispatchId: 3, PowerOffApower: ""},
- {StartHourTime: "14:00", EndHourTime: "16:00", WaveType: 1, Name: "mid-peak", BriefDescribe: "Self-consumption", SolarPriority: "1,3,2", LoadPriority: "1,3,2", MinDischargeSoc: &int90, SolarCutoff: &int0, UseModeFlag: 0, DispatchId: 6, PowerOffApower: ""},
- {StartHourTime: "16:00", EndHourTime: "18:00", WaveType: 2, Name: "on-peak", BriefDescribe: "Self-consumption", SolarPriority: "1,3,2", LoadPriority: "1,3,2", MinDischargeSoc: &int90, SolarCutoff: &int0, UseModeFlag: 0, DispatchId: 6, PowerOffApower: ""},
- {StartHourTime: "18:00", EndHourTime: "21:00", WaveType: 2, Name: "on-peak", BriefDescribe: "Zero Import", SolarPriority: "9,9,9", GridDischargeMax: &float0_1, GridFeedMax: &float0_1, DischargePower: &float10, GridMax: &float0, GcaoMax: &float0, LoadPriority: "9,9,9", HeatEnable: &int1, SolarCutoff: &int0, UseModeFlag: 0, DispatchId: 9, PowerOffApower: ""},
- {StartHourTime: "21:00", EndHourTime: "23:00", WaveType: 2, Name: "on-peak", BriefDescribe: "Self-consumption", SolarPriority: "1,3,2", LoadPriority: "1,3,2", SolarCutoff: &int0, UseModeFlag: 0, DispatchId: 6, PowerOffApower: ""},
- {StartHourTime: "23:00", EndHourTime: "24:00", WaveType: 1, Name: "mid-peak", BriefDescribe: "Self-consumption", SolarPriority: "1,3,2", LoadPriority: "1,3,2", SolarCutoff: &int0, UseModeFlag: 0, DispatchId: 6, PowerOffApower: ""},
- },
- },
- },
- }
- // 模版 4478 - FOUR4FREE (GloBird Energy, Queensland)
- template4478 := TemplateData{
- Template: TemplateInfo{
- ID: 4478, ElectricCompany: "GloBird Energy", EleCompanyFullName: "GloBird Energy",
- DerSchdule: "FOUR4FREE", GridType: "FOUR4FREE", Name: "FOUR4FREE",
- ElectricityType: 1, CountryId: 3, ProvinceId: 88, ProvinceEn: "Queensland",
- Env: 0, MookRunCount: 0, Priority: 0,
- },
- StrategyList: []StrategyInfo{
- {
- SeasonName: "All months", Month: "1,2,3,4,5,6,7,8,9,10,11,12",
- DayName: "Every day", DayType: 3, CcociateSn: "", CcociateType: 1,
- DetailVoList: []DetailInfo{
- {StartHourTime: "00:00", EndHourTime: "10:00", WaveType: 1, Name: "mid-peak", BriefDescribe: "Self-consumption", SolarPriority: "1,3,2", LoadPriority: "1,3,2", SolarCutoff: &int0, UseModeFlag: 0, DispatchId: 6, PowerOffApower: ""},
- {StartHourTime: "10:00", EndHourTime: "14:00", WaveType: 0, Name: "off-peak", BriefDescribe: "aPower charge from solar", SolarPriority: "1,2,3", GridChargeMax: &float5, LoadPriority: "1,2,0", SolarCutoff: &int0, UseModeFlag: 0, DispatchId: 3, PowerOffApower: ""},
- {StartHourTime: "14:00", EndHourTime: "16:00", WaveType: 1, Name: "mid-peak", BriefDescribe: "Self-consumption", SolarPriority: "1,3,2", LoadPriority: "1,3,2", MinDischargeSoc: &int90, SolarCutoff: &int0, UseModeFlag: 0, DispatchId: 6, PowerOffApower: ""},
- {StartHourTime: "16:00", EndHourTime: "23:00", WaveType: 2, Name: "on-peak", BriefDescribe: "Self-consumption", SolarPriority: "1,3,2", LoadPriority: "1,3,2", SolarCutoff: &int0, UseModeFlag: 0, DispatchId: 6, PowerOffApower: ""},
- {StartHourTime: "23:00", EndHourTime: "24:00", WaveType: 1, Name: "mid-peak", BriefDescribe: "Self-consumption", SolarPriority: "1,3,2", LoadPriority: "1,3,2", SolarCutoff: &int0, UseModeFlag: 0, DispatchId: 6, PowerOffApower: ""},
- },
- },
- },
- }
- // 模版 4479 - Basic 3 Free (OVO Energy, Queensland)
- template4479 := TemplateData{
- Template: TemplateInfo{
- ID: 4479, ElectricCompany: "OVO Energy", EleCompanyFullName: "OVO Energy",
- DerSchdule: "Basic 3 Free", GridType: "Basic 3 Free", Name: "Basic 3 Free",
- ElectricityType: 1, CountryId: 3, ProvinceId: 88, ProvinceEn: "Queensland",
- Env: 0, MookRunCount: 0, Priority: 0,
- },
- StrategyList: []StrategyInfo{
- {
- SeasonName: "All months", Month: "1,2,3,4,5,6,7,8,9,10,11,12",
- DayName: "Every day", DayType: 3, CcociateSn: "", CcociateType: 1,
- DetailVoList: []DetailInfo{
- {StartHourTime: "00:00", EndHourTime: "11:00", WaveType: 1, Name: "mid-peak", BriefDescribe: "Self-consumption", SolarPriority: "1,3,2", LoadPriority: "1,3,2", SolarCutoff: &int0, UseModeFlag: 0, DispatchId: 6, PowerOffApower: ""},
- {StartHourTime: "11:00", EndHourTime: "14:00", WaveType: 0, Name: "off-peak", BriefDescribe: "aPower charge from solar", SolarPriority: "1,2,3", GridChargeMax: &float5, LoadPriority: "1,2,0", SolarCutoff: &int0, UseModeFlag: 0, DispatchId: 3, PowerOffApower: ""},
- {StartHourTime: "14:00", EndHourTime: "16:00", WaveType: 1, Name: "mid-peak", BriefDescribe: "Self-consumption", SolarPriority: "1,3,2", LoadPriority: "1,3,2", MinDischargeSoc: &int90, SolarCutoff: &int0, UseModeFlag: 0, DispatchId: 6, PowerOffApower: ""},
- {StartHourTime: "16:00", EndHourTime: "23:00", WaveType: 2, Name: "on-peak", BriefDescribe: "self-consumption", SolarPriority: "1,3,2", LoadPriority: "1,3,2", SolarCutoff: &int0, UseModeFlag: 0, DispatchId: 6, PowerOffApower: ""},
- {StartHourTime: "23:00", EndHourTime: "24:00", WaveType: 1, Name: "mid-peak", BriefDescribe: "self-consumption", SolarPriority: "1,3,2", LoadPriority: "1,3,2", SolarCutoff: &int0, UseModeFlag: 0, DispatchId: 6, PowerOffApower: ""},
- },
- },
- },
- }
- return []TemplateData{template4477, template4478, template4479}
- }
- // ===== HAR 数据:澳洲所有州 =====
- func getAustraliaProvinces() []Province {
- return []Province{
- {ID: 93, NameEn: "Australian Capital Territory", NameZh: "澳大利亚首都特区"},
- {ID: 95, NameEn: "Jervis Bay Territory", NameZh: "杰维斯湾领地"},
- {ID: 87, NameEn: "New South Wales", NameZh: "新南威尔士州"},
- {ID: 94, NameEn: "Northern Territory", NameZh: "北领地"},
- {ID: 88, NameEn: "Queensland", NameZh: "昆士兰州"},
- {ID: 89, NameEn: "South Australia", NameZh: "南澳大利亚州"},
- {ID: 90, NameEn: "Tasmania", NameZh: "塔斯马尼亚州"},
- {ID: 91, NameEn: "Victoria", NameZh: "維多利亞省"},
- {ID: 92, NameEn: "Western Australia", NameZh: "西澳大利亚州"},
- }
- }
- // ===== HTTP 客户端 =====
- type Client struct {
- httpClient *http.Client
- token string
- }
- func NewClient(token string) *Client {
- return &Client{
- httpClient: &http.Client{Timeout: 30 * time.Second},
- token: token,
- }
- }
- // POST JSON 请求
- func (c *Client) PostJSON(path string, payload interface{}) ([]byte, error) {
- reqURL := baseURL + path
- jsonData, err := json.Marshal(payload)
- if err != nil {
- return nil, fmt.Errorf("序列化请求体失败: %w", err)
- }
- req, err := http.NewRequest("POST", reqURL, bytes.NewBuffer(jsonData))
- if err != nil {
- return nil, fmt.Errorf("创建请求失败: %w", err)
- }
- req.Header.Set("Content-Type", "application/json")
- req.Header.Set("Logintoken", c.token)
- req.Header.Set("Lang", lang)
- req.Header.Set("Accept", "application/json, text/plain, */*")
- resp, err := c.httpClient.Do(req)
- if err != nil {
- return nil, fmt.Errorf("请求失败: %w", err)
- }
- defer resp.Body.Close()
- body, err := io.ReadAll(resp.Body)
- if err != nil {
- return nil, fmt.Errorf("读取响应失败: %w", err)
- }
- if resp.StatusCode != http.StatusOK {
- return nil, fmt.Errorf("HTTP状态码: %d, 响应: %s", resp.StatusCode, string(body))
- }
- return body, nil
- }
- // ===== 业务逻辑 =====
- // 创建模版
- func createTemplate(client *Client, payload *CreateTemplatePayload) (int, error) {
- body, err := client.PostJSON("/hes-gateway/manage/updateOrSaveIotTouTemplate", payload)
- if err != nil {
- return 0, err
- }
- var resp APIResponse
- if err := json.Unmarshal(body, &resp); err != nil {
- return 0, fmt.Errorf("解析创建模版响应失败: %w", err)
- }
- if resp.Code != 200 {
- return 0, fmt.Errorf("创建模版失败: %s", resp.Message)
- }
- var newID int
- if err := json.Unmarshal(resp.Result, &newID); err != nil {
- return 0, fmt.Errorf("解析新模版ID失败: %w", err)
- }
- return newID, nil
- }
- // 构建创建模版的请求体
- func buildCreatePayload(tmplData *TemplateData, province Province) *CreateTemplatePayload {
- return &CreateTemplatePayload{
- Template: CreateTemplateInfo{
- CountryId: tmplData.Template.CountryId,
- ProvinceId: province.ID,
- DerSchdule: tmplData.Template.DerSchdule,
- EleCompanyFullName: tmplData.Template.EleCompanyFullName,
- ElectricCompany: tmplData.Template.ElectricCompany,
- GridType: tmplData.Template.GridType,
- ElectricityType: tmplData.Template.ElectricityType,
- Name: tmplData.Template.Name,
- MookRunCount: fmt.Sprintf("%d", tmplData.Template.MookRunCount),
- Priority: fmt.Sprintf("%d", tmplData.Template.Priority),
- Env: fmt.Sprintf("%d", tmplData.Template.Env),
- },
- StrategyList: tmplData.StrategyList,
- Lang: lang,
- }
- }
- func main() {
- // 从环境变量获取 token
- token := os.Getenv("FWH_TOKEN")
- if token == "" {
- fmt.Println("请设置 FWH_TOKEN 环境变量")
- fmt.Println("例如: export FWH_TOKEN=eyJhbGciOiJIUzI1NiIs...")
- os.Exit(1)
- }
- client := NewClient(token)
- // 获取硬编码的模版数据和州列表
- templates := getTemplateData()
- allProvinces := getAustraliaProvinces()
- // 收集已有模版对应的州ID(Queensland, id=88)
- existingProvinceIDs := make(map[int]bool)
- for _, tmpl := range templates {
- existingProvinceIDs[tmpl.Template.ProvinceId] = true
- }
- // 过滤出需要创建的州
- var targetProvinces []Province
- for _, p := range allProvinces {
- if !existingProvinceIDs[p.ID] {
- targetProvinces = append(targetProvinces, p)
- }
- }
- fmt.Printf("模版数量: %d, 目标州数量: %d (已排除: Queensland)\n", len(templates), len(targetProvinces))
- fmt.Printf("预计创建模版总数: %d\n\n", len(templates)*len(targetProvinces))
- // 为每个目标州创建所有模版
- successCount := 0
- failCount := 0
- for _, province := range targetProvinces {
- fmt.Printf("--- 州: %s (%s) ---\n", province.NameEn, province.NameZh)
- for _, tmplData := range templates {
- payload := buildCreatePayload(&tmplData, province)
- newID, err := createTemplate(client, payload)
- if err != nil {
- fmt.Printf(" ❌ [%s] 创建失败: %v\n", tmplData.Template.Name, err)
- failCount++
- continue
- }
- fmt.Printf(" ✅ [%s] 创建成功, 新ID: %d\n", tmplData.Template.Name, newID)
- successCount++
- // 请求间隔,避免过快
- time.Sleep(500 * time.Millisecond)
- }
- fmt.Println()
- }
- // 输出统计
- fmt.Printf("===== 完成 =====\n")
- fmt.Printf("成功: %d, 失败: %d, 总计: %d\n", successCount, failCount, successCount+failCount)
- }
|