package service import ( "context" "encoding/json" "fmt" "with_you/internal/grpc/runner" "with_you/internal/model" "with_you/internal/repository" pb "with_you/proto/runner" "github.com/google/uuid" "go.uber.org/zap" ) type SyncExamsRequest struct { Username string `json:"username" binding:"required"` Password string `json:"password" binding:"required"` Semester string `json:"semester"` } type SyncExamsResult struct { Success bool `json:"success"` Message string `json:"message"` SyncedCount int `json:"synced_count"` ErrorMessage string `json:"error_message,omitempty"` } type ExamSyncService interface { SyncUserExams(ctx context.Context, userID string, req *SyncExamsRequest) (*SyncExamsResult, error) ListExams(userID, semester string) ([]*model.Exam, error) } type examSyncService struct { taskManager *runner.TaskManager examRepo repository.ExamRepository logger *zap.Logger } func NewExamSyncService( taskManager *runner.TaskManager, examRepo repository.ExamRepository, logger *zap.Logger, ) ExamSyncService { return &examSyncService{ taskManager: taskManager, examRepo: examRepo, logger: logger, } } func (s *examSyncService) SyncUserExams(ctx context.Context, userID string, req *SyncExamsRequest) (*SyncExamsResult, error) { payload := &pb.GetExamsPayload{ Username: req.Username, Password: req.Password, Semester: req.Semester, } taskResult, err := s.taskManager.SubmitTaskSync(ctx, pb.TaskType_TASK_TYPE_GET_EXAMS, payload) if err != nil { s.logger.Error("failed to submit exams sync task", zap.String("user_id", userID), zap.Error(err)) return nil, fmt.Errorf("failed to submit task: %w", err) } if taskResult.Status != pb.TaskStatus_TASK_STATUS_SUCCESS { return &SyncExamsResult{ Success: false, Message: "考试安排获取失败", ErrorMessage: taskResult.ErrorMessage, }, nil } var resultData pb.ExamsResultData if err := json.Unmarshal(taskResult.Data, &resultData); err != nil { s.logger.Error("failed to parse exams result", zap.String("user_id", userID), zap.Error(err)) return nil, fmt.Errorf("failed to parse result: %w", err) } exams := s.convertExams(userID, &resultData) if err := s.saveExams(ctx, userID, resultData.Semester, exams); err != nil { s.logger.Error("failed to save exams", zap.String("user_id", userID), zap.Error(err)) return nil, fmt.Errorf("failed to save exams: %w", err) } s.logger.Info("exams sync completed", zap.String("user_id", userID), zap.Int("exam_count", len(exams))) return &SyncExamsResult{ Success: true, Message: "考试安排同步成功", SyncedCount: len(exams), }, nil } func (s *examSyncService) convertExams(userID string, data *pb.ExamsResultData) []*model.Exam { exams := make([]*model.Exam, 0, len(data.Exams)) for _, e := range data.Exams { exams = append(exams, &model.Exam{ ID: uuid.New().String(), UserID: userID, Semester: data.Semester, CourseName: e.CourseName, ExamType: e.ExamType, Date: e.Date, Time: e.Time, Week: e.Week, Weekday: e.Weekday, }) } return exams } func (s *examSyncService) saveExams(ctx context.Context, userID, semester string, exams []*model.Exam) error { if err := s.examRepo.DeleteByUserAndSemester(ctx, userID, semester); err != nil { return fmt.Errorf("failed to delete old exams: %w", err) } if len(exams) == 0 { return nil } if err := s.examRepo.BatchCreate(ctx, exams); err != nil { return fmt.Errorf("failed to create exams: %w", err) } return nil } // ListExams 查询用户某学期的考试安排。 func (s *examSyncService) ListExams(userID, semester string) ([]*model.Exam, error) { return s.examRepo.ListByUserAndSemester(userID, semester) }