refactor(grpc): restructure task handling and migrate to slog
All checks were successful
Build / build (push) Successful in 5m28s

Refactor the gRPC client and task handling logic to improve modularity,
error handling, and observability.

- Split `grpc/handler.go` into specialized handler files (login,
  classroom, exams, grades, schedule) to reduce complexity.
- Replace standard `log` with `log/slog` throughout the project for
  structured logging.
- Refactor `grpc/client.go` to use a thread-safe connection management
  pattern with `sync.RWMutex`.
- Remove the legacy `service` package and HTTP server implementation,
  transitioning the application to a pure gRPC runner mode.
- Clean up `config` and `pkg/errors` to remove unused utilities and
  simplify the API.
- Improve error reporting in parsers and HTTP clients by checking
  response status codes.
This commit is contained in:
2026-06-09 23:08:03 +08:00
parent ee5c36f3ac
commit accc6567f2
26 changed files with 720 additions and 1442 deletions

View File

@@ -1,11 +1,9 @@
package parser
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"regexp"
"sort"
"strconv"
@@ -26,17 +24,11 @@ type weekRoomEntry struct {
}
// 获取课表
func FetchSchedule(httpClient *http.Client) ([]models.Course, error) {
return FetchScheduleWithClient(httpClient, config.Semester)
}
// FetchScheduleWithClient 使用指定的 HTTP 客户端和学期获取课表
func FetchScheduleWithClient(httpClient *http.Client, semester string) ([]models.Course, error) {
fmt.Println("[5] 获取课表数据...")
// POST请求获取课表
data := fmt.Sprintf("xnxq=%s", semester)
req, err := http.NewRequest("POST", config.BaseURL+"/kbcx/queryGrkb", strings.NewReader(data))
req, err := http.NewRequest("POST", config.Get().BaseURL+"/kbcx/queryGrkb", strings.NewReader(data))
if err != nil {
return nil, err
}
@@ -54,35 +46,7 @@ func FetchScheduleWithClient(httpClient *http.Client, semester string) ([]models
return nil, err
}
// 解析课表
courses, err := ParseScheduleHTML(string(body))
if err != nil {
return nil, err
}
fmt.Printf(" 共提取 %d 门课程\n", len(courses))
// 输出到JSON
jsonData, err := os.Create("schedule_result.json")
if err != nil {
return nil, err
}
defer jsonData.Close()
encoder := json.NewEncoder(jsonData)
encoder.SetIndent("", " ")
err = encoder.Encode(courses)
if err != nil {
return nil, err
}
fmt.Println(" JSON结果已保存到: schedule_result.json")
// 打印课程信息
for i, course := range courses {
fmt.Printf(" 课程 %d: %s - %s\n", i+1, course.Name, course.Teacher)
}
return courses, nil
return ParseScheduleHTML(string(body))
}
// 解析课表HTML
@@ -354,27 +318,6 @@ func buildCourseKey(name, teacher, room string) string {
return strings.TrimSpace(name) + "|" + strings.TrimSpace(teacher) + "|" + strings.TrimSpace(room)
}
// extractWeekInfo 提取一段文本中的完整周次片段
// 例如:于战华[15]周,[2-14]周 -> before=于战华, weeks=[15]周,[2-14]周, after=
func extractWeekInfo(text string) (beforeWeek, weeksPart, afterWeek string, ok bool) {
indexes := weekTokenRegexp.FindAllStringIndex(text, -1)
if len(indexes) == 0 {
return "", "", "", false
}
firstStart := indexes[0][0]
beforeWeek = strings.TrimSpace(text[:firstStart])
parts := weekTokenRegexp.FindAllString(text, -1)
weeksPart = strings.Join(parts, ",")
// 周次后(含中间)非周次内容作为教室候选
tail := strings.TrimSpace(text[firstStart:])
afterWeek = strings.TrimSpace(weekTokenRegexp.ReplaceAllString(tail, ""))
afterWeek = strings.Trim(afterWeek, ", ")
return beforeWeek, weeksPart, afterWeek, true
}
// extractWeekEntries 将一段文本拆成多个“周次-地点”片段
// 例如:李鹏程[2-8]周N楼-335[9]周N楼-331
// 会拆成: