package service import ( "context" "log/slog" "schedule_converter/client" "schedule_converter/models" "schedule_converter/parser" apperr "schedule_converter/pkg/errors" pb "schedule_converter/proto/runner" ) // GetGrades 获取成绩:登录 → 优先 GPA 页面 → 回退到成绩页面 → 解析 → 转换为 pb 结果。 func (s *Service) GetGrades(ctx context.Context, req *pb.GetGradesPayload) (*pb.GradesResultData, error) { log := slog.With("username", req.Username, "semester", req.Semester) httpClient, err := s.newAuthenticatedClient(ctx, req.Username, req.Password, "") if err != nil { return nil, err } var gpaSummary *pb.GpaSummary gpaHTML, err := client.FetchHTML(ctx, httpClient, "GET", "/xfj/queryListXfj", nil, "", "") if err != nil { log.Warn("获取GPA页面失败,尝试成绩页面", "error", err) gradesHTML, ferr := client.FetchHTML(ctx, httpClient, "GET", "/cjcx/queryQmcj", nil, "", "") if ferr != nil { return nil, apperr.New("fetch_grades", ferr, "获取成绩失败") } grades, perr := parser.ParseGradesHTML(gradesHTML) if perr != nil { return nil, apperr.New("parse_grades", perr, "解析成绩失败") } return &pb.GradesResultData{ StudentId: req.Username, Semester: req.Semester, Grades: convertGrades(grades), }, nil } summary, grades, err := parser.ParseGPAHTML(gpaHTML) if err != nil { return nil, apperr.New("parse_gpa", err, "解析学分绩失败") } if summary != nil { gpaSummary = &pb.GpaSummary{ AverageGpa: summary.AverageGPA, Rank: summary.Rank, ExamTotalGpa: summary.ExamTotalGPA, ExamTotalCredits: summary.ExamTotalCredits, FailCredits: summary.FailCredits, Semesters: summary.Semesters, } } return &pb.GradesResultData{ StudentId: req.Username, Semester: req.Semester, Grades: convertGrades(grades), GpaSummary: gpaSummary, }, nil } // convertGrades 将内部成绩模型转换为 pb.Grade 列表。 func convertGrades(grades []models.GradeEntry) []*pb.Grade { result := make([]*pb.Grade, 0, len(grades)) for _, g := range grades { result = append(result, &pb.Grade{ Term: g.Term, CourseCode: g.Code, CourseName: g.Name, Nature: g.Nature, Category: g.Category, Credit: g.Credit, Score: g.Score, GradePoint: g.GradePoint, IsExam: g.IsExam, }) } return result }