feat(runner): implement empty classroom sync functionality
Add support for fetching and synchronizing empty classroom data. This includes new protobuf definitions for task payloads and results, database models, repository, service, and HTTP handlers. - Add `TASK_TYPE_GET_EMPTY_CLASSROOM` to `TaskType` enum - Implement `EmptyClassroom` model and auto-migration - Add `EmptyClassroomHandler` with `GET` and `POST /sync` routes - Implement `EmptyClassroomSyncService` and `EmptyClassroomRepository` - Update dependency injection with wire sets
This commit is contained in:
135
internal/service/empty_classroom_sync_service.go
Normal file
135
internal/service/empty_classroom_sync_service.go
Normal file
@@ -0,0 +1,135 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user