Skip to content

Commit 108d1a1

Browse files
committed
feat: add schedule data retrieval and export functionality
1 parent 121912c commit 108d1a1

File tree

3 files changed

+271
-0
lines changed

3 files changed

+271
-0
lines changed

internal/analyze/schedule.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package analyze
2+
3+
import (
4+
"encoding/csv"
5+
"encoding/json"
6+
"github.com/kataras/iris/v12/x/errors"
7+
"github.com/scutrobotlab/rm-schedule/internal/job"
8+
"github.com/scutrobotlab/rm-schedule/internal/types"
9+
"io"
10+
"net/http"
11+
"os"
12+
"strconv"
13+
)
14+
15+
// GetScheduleData 获取赛程数据
16+
func GetScheduleData() ([]byte, error) {
17+
resp, err := http.Get(job.ScheduleUrl)
18+
if err != nil {
19+
return nil, err
20+
}
21+
defer resp.Body.Close()
22+
23+
if resp.StatusCode != http.StatusOK {
24+
return nil, errors.New(resp.Status)
25+
}
26+
27+
return io.ReadAll(resp.Body)
28+
}
29+
30+
// GetSchedule 获取赛程数据并解析为 ScheduleResp 结构体
31+
func GetSchedule() (*types.ScheduleResp, error) {
32+
data, err := GetScheduleData()
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
var schedule types.ScheduleResp
38+
if err := json.Unmarshal(data, &schedule); err != nil {
39+
return nil, err
40+
}
41+
42+
return &schedule, nil
43+
}
44+
45+
// ExportScheduleToFile 导出赛程数据到文件
46+
func ExportScheduleToFile(filename string) error {
47+
schedule, err := GetSchedule()
48+
if err != nil {
49+
return err
50+
}
51+
52+
file, err := os.Create(filename)
53+
if err != nil {
54+
return err
55+
}
56+
defer file.Close()
57+
58+
writer := csv.NewWriter(file)
59+
defer writer.Flush()
60+
61+
writer.Write([]string{
62+
"序号",
63+
"赛季",
64+
"赛区",
65+
"赛次",
66+
"比赛阶段",
67+
"红方学校",
68+
"红队名",
69+
"蓝方学校",
70+
"蓝队名",
71+
"红方比分",
72+
"蓝方比分",
73+
"备注",
74+
})
75+
76+
writeMatch := func(zone types.ZoneNode, match types.MatchNode) {
77+
writer.Write([]string{
78+
match.ID,
79+
"2025",
80+
zone.Name,
81+
strconv.Itoa(match.OrderNumber),
82+
match.MatchType,
83+
match.RedSide.Player.Team.CollegeName,
84+
match.RedSide.Player.Team.Name,
85+
match.BlueSide.Player.Team.CollegeName,
86+
match.BlueSide.Player.Team.Name,
87+
strconv.Itoa(match.RedSideWinGameCount),
88+
strconv.Itoa(match.BlueSideWinGameCount),
89+
"",
90+
})
91+
}
92+
93+
event := schedule.Data.Event
94+
for _, zone := range event.Zones.Nodes {
95+
for _, match := range zone.GroupMatches.Nodes {
96+
writeMatch(zone, match)
97+
}
98+
for _, match := range zone.KnockoutMatches.Nodes {
99+
writeMatch(zone, match)
100+
}
101+
}
102+
103+
return nil
104+
}

internal/analyze/schedule_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package analyze
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
"time"
7+
)
8+
9+
func TestGetScheduleData(t *testing.T) {
10+
schedule, err := GetScheduleData()
11+
if err != nil {
12+
t.Fatalf("Failed to get schedule: %v", err)
13+
}
14+
if len(schedule) == 0 {
15+
t.Fatal("Schedule data is empty")
16+
}
17+
t.Logf("Schedule data length: %d bytes", len(schedule))
18+
fmt.Printf("%s\n", schedule)
19+
}
20+
21+
func TestGetSchedule(t *testing.T) {
22+
schedule, err := GetSchedule()
23+
if err != nil {
24+
t.Fatalf("Failed to get schedule: %v", err)
25+
}
26+
if schedule == nil {
27+
t.Fatal("Schedule data is nil")
28+
}
29+
t.Logf("Schedule data: %+v", schedule)
30+
}
31+
32+
func TestExportScheduleToFile(t *testing.T) {
33+
filename := fmt.Sprintf("schedule_%s.csv", time.Now().Format("20060102_150405"))
34+
if err := ExportScheduleToFile(filename); err != nil {
35+
t.Fatalf("Failed to export schedule to file: %v", err)
36+
}
37+
t.Logf("Schedule exported to file: %s", filename)
38+
}

internal/types/schedule.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package types
2+
3+
type ScheduleResp struct {
4+
Data ScheduleData `json:"data"`
5+
}
6+
7+
type ScheduleData struct {
8+
Event Event `json:"event"`
9+
LastEvent LastEvent `json:"last_event"`
10+
}
11+
12+
type Event struct {
13+
Title string `json:"title"`
14+
Zones Zones `json:"zones"`
15+
}
16+
17+
type Zones struct {
18+
Nodes []ZoneNode `json:"nodes"`
19+
}
20+
21+
type ZoneNode struct {
22+
ID string `json:"id"`
23+
MatchDates []string `json:"matchDates"`
24+
Name string `json:"name"`
25+
ZoneType string `json:"zoneType"`
26+
Groups Groups `json:"groups"`
27+
GroupMatches Matches `json:"groupMatches"`
28+
KnockoutMatches Matches `json:"knockoutMatches"`
29+
}
30+
31+
type Groups struct {
32+
Nodes []GroupNode `json:"nodes"`
33+
}
34+
35+
type GroupNode struct {
36+
ID string `json:"id"`
37+
Name string `json:"name"`
38+
Players Players `json:"players"`
39+
}
40+
41+
type Players struct {
42+
Nodes []PlayerNode `json:"nodes"`
43+
}
44+
45+
type PlayerNode struct {
46+
ID string `json:"id"`
47+
Name string `json:"name"`
48+
Rank int `json:"rank"`
49+
Score int `json:"score"`
50+
TeamID *string `json:"teamId,omitempty"`
51+
Team *Team `json:"team,omitempty"`
52+
}
53+
54+
type Team struct {
55+
ID string `json:"id"`
56+
Name string `json:"name"`
57+
CollegeLogo string `json:"collegeLogo"`
58+
CollegeName string `json:"collegeName"`
59+
}
60+
61+
type Matches struct {
62+
Nodes []MatchNode `json:"nodes"`
63+
}
64+
65+
type MatchNode struct {
66+
ID string `json:"id"`
67+
GroupID string `json:"groupId"`
68+
MatchType string `json:"matchType"`
69+
OrderNumber int `json:"orderNumber"`
70+
PlanGameCount int `json:"planGameCount"`
71+
PlanStartedAt string `json:"planStartedAt"`
72+
Result string `json:"result"`
73+
Slug interface{} `json:"slug"`
74+
SlugName string `json:"slugName"`
75+
Status string `json:"status"`
76+
WinnerPlaceholdName interface{} `json:"winnerPlaceholdName"`
77+
LoserPlaceholdName interface{} `json:"loserPlaceholdName"`
78+
BlueSideID string `json:"blueSideId"`
79+
BlueSideScore int `json:"blueSideScore"`
80+
BlueSideWinGameCount int `json:"blueSideWinGameCount"`
81+
BlueSide BlueSide `json:"blueSide"`
82+
RedSideID string `json:"redSideId"`
83+
RedSideScore int `json:"redSideScore"`
84+
RedSideWinGameCount int `json:"redSideWinGameCount"`
85+
RedSide RedSide `json:"redSide"`
86+
}
87+
88+
type BlueSide struct {
89+
ID string `json:"id"`
90+
PreparedStatus string `json:"preparedStatus"`
91+
FillSourceID *string `json:"fillSourceId,omitempty"`
92+
FillSourceType *string `json:"fillSourceType,omitempty"`
93+
FillSourceNumber *int `json:"fillSourceNumber,omitempty"`
94+
FillStatus string `json:"fillStatus"`
95+
PlayerID *string `json:"playerId,omitempty"`
96+
Player *Player `json:"player,omitempty"`
97+
UpdatedAt string `json:"updatedAt"`
98+
}
99+
100+
type Player struct {
101+
ID string `json:"id"`
102+
Name string `json:"name"`
103+
Rank int `json:"rank"`
104+
Score int `json:"score"`
105+
TeamID *string `json:"teamId,omitempty"`
106+
Team *Team `json:"team,omitempty"`
107+
}
108+
109+
type PlayerWithMatch struct {
110+
Player Player `json:"player"`
111+
Match MatchNode `json:"match"`
112+
}
113+
114+
type RedSide struct {
115+
ID string `json:"id"`
116+
PreparedStatus string `json:"preparedStatus"`
117+
FillSourceID *string `json:"fillSourceId,omitempty"`
118+
FillSourceType *string `json:"fillSourceType,omitempty"`
119+
FillSourceNumber *int `json:"fillSourceNumber,omitempty"`
120+
FillStatus string `json:"fillStatus"`
121+
PlayerID *string `json:"playerId,omitempty"`
122+
Player *Player `json:"player,omitempty"`
123+
UpdatedAt string `json:"updatedAt"`
124+
}
125+
126+
type LastEvent struct {
127+
Title string `json:"title"`
128+
Zones Zones `json:"zones"`
129+
}

0 commit comments

Comments
 (0)