package service import ( "context" "fmt" "log/slog" "strings" "schedule_converter/client" "schedule_converter/models" "schedule_converter/parser" apperr "schedule_converter/pkg/errors" pb "schedule_converter/proto/runner" ) var allExamTypes = []models.ExamType{models.ExamTypeFinal, models.ExamTypeMidterm, models.ExamTypeMakeup} // GetExams 获取考试安排:登录 → 遍历各考试类型抓取并解析 → 合并 → 转换为 pb 结果。 func (s *Service) GetExams(ctx context.Context, req *pb.GetExamsPayload) (*pb.ExamsResultData, 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 allExams []models.ExamEntry var lastErr error for _, et := range allExamTypes { data := fmt.Sprintf("xnxq=&kssjd=%s", parser.ExamTypeCode(et)) html, ferr := client.FetchHTML(ctx, httpClient, "POST", "/kscx/queryKsForXs", strings.NewReader(data), "application/x-www-form-urlencoded", "") if ferr != nil { log.Warn("获取考试数据失败", "exam_type", string(et), "error", ferr) lastErr = ferr continue } exams, perr := parser.ParseExamHTML(html, et) if perr != nil { log.Warn("解析考试数据失败", "exam_type", string(et), "error", perr) lastErr = perr continue } allExams = append(allExams, exams...) } if len(allExams) == 0 && lastErr != nil { return nil, apperr.New("fetch_exams", lastErr, "所有考试类型获取均失败") } return &pb.ExamsResultData{ StudentId: req.Username, Semester: req.Semester, Exams: convertExams(allExams), }, nil } // convertExams 将内部考试模型转换为 pb.Exam 列表。 func convertExams(exams []models.ExamEntry) []*pb.Exam { result := make([]*pb.Exam, 0, len(exams)) for _, e := range exams { examTypeStr := "期末" switch e.Type { case models.ExamTypeMidterm: examTypeStr = "期中" case models.ExamTypeMakeup: examTypeStr = "补考" } result = append(result, &pb.Exam{ CourseName: e.Course, ExamType: examTypeStr, Date: e.Date, Time: e.Time, Week: e.Week, Weekday: e.Weekday, }) } return result }