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 SyncEmptyClassroomsRequest struct { Username string `json:"username" binding:"required"` Password string `json:"password" binding:"required"` Semester string `json:"semester"` WeekStart string `json:"week_start"` WeekEnd string `json:"week_end"` CampusCode string `json:"campus_code"` } type SyncEmptyClassroomsResult struct { Success bool `json:"success"` Message string `json:"message"` SyncedCount int `json:"synced_count"` ErrorMessage string `json:"error_message,omitempty"` } type EmptyClassroomSyncService interface { SyncUserEmptyClassrooms(ctx context.Context, userID string, req *SyncEmptyClassroomsRequest) (*SyncEmptyClassroomsResult, error) } type emptyClassroomSyncService struct { taskManager *runner.TaskManager classroomRepo repository.EmptyClassroomRepository logger *zap.Logger } func NewEmptyClassroomSyncService( taskManager *runner.TaskManager, classroomRepo repository.EmptyClassroomRepository, logger *zap.Logger, ) EmptyClassroomSyncService { return &emptyClassroomSyncService{ taskManager: taskManager, classroomRepo: classroomRepo, logger: logger, } } func (s *emptyClassroomSyncService) SyncUserEmptyClassrooms(ctx context.Context, userID string, req *SyncEmptyClassroomsRequest) (*SyncEmptyClassroomsResult, error) { payload := &pb.GetEmptyClassroomPayload{ Username: req.Username, Password: req.Password, Semester: req.Semester, WeekStart: req.WeekStart, WeekEnd: req.WeekEnd, CampusCode: req.CampusCode, } taskResult, err := s.taskManager.SubmitTaskSync(ctx, pb.TaskType_TASK_TYPE_GET_EMPTY_CLASSROOM, payload) if err != nil { s.logger.Error("failed to submit empty classroom 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 &SyncEmptyClassroomsResult{ Success: false, Message: "空教室查询失败", ErrorMessage: taskResult.ErrorMessage, }, nil } var resultData pb.EmptyClassroomResultData if err := json.Unmarshal(taskResult.Data, &resultData); err != nil { s.logger.Error("failed to parse empty classroom result", zap.String("user_id", userID), zap.Error(err)) return nil, fmt.Errorf("failed to parse result: %w", err) } classrooms := s.convertClassrooms(userID, &resultData) if err := s.saveClassrooms(ctx, userID, resultData.Semester, classrooms); err != nil { s.logger.Error("failed to save empty classrooms", zap.String("user_id", userID), zap.Error(err)) return nil, fmt.Errorf("failed to save classrooms: %w", err) } s.logger.Info("empty classrooms sync completed", zap.String("user_id", userID), zap.Int("classroom_count", len(classrooms))) return &SyncEmptyClassroomsResult{ Success: true, Message: "空教室查询成功", SyncedCount: len(classrooms), }, nil } func (s *emptyClassroomSyncService) convertClassrooms(userID string, data *pb.EmptyClassroomResultData) []*model.EmptyClassroom { classrooms := make([]*model.EmptyClassroom, 0, len(data.Classrooms)) for _, c := range data.Classrooms { classrooms = append(classrooms, &model.EmptyClassroom{ ID: uuid.New().String(), UserID: userID, Semester: data.Semester, Classroom: c.Classroom, Weekday: c.Weekday, Periods: c.Periods, Weeks: c.Weeks, }) } return classrooms } func (s *emptyClassroomSyncService) saveClassrooms(ctx context.Context, userID, semester string, classrooms []*model.EmptyClassroom) error { if err := s.classroomRepo.DeleteByUserAndSemester(ctx, userID, semester); err != nil { return fmt.Errorf("failed to delete old classrooms: %w", err) } if len(classrooms) == 0 { return nil } if err := s.classroomRepo.BatchCreate(ctx, classrooms); err != nil { return fmt.Errorf("failed to create classrooms: %w", err) } return nil }