Files
backend/internal/service/grade_sync_service.go

161 lines
4.4 KiB
Go
Raw Normal View History

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 SyncGradesRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
Semester string `json:"semester"`
}
type SyncGradesResult struct {
Success bool `json:"success"`
Message string `json:"message"`
SyncedCount int `json:"synced_count"`
ErrorMessage string `json:"error_message,omitempty"`
}
type GradeSyncService interface {
SyncUserGrades(ctx context.Context, userID string, req *SyncGradesRequest) (*SyncGradesResult, error)
}
type gradeSyncService struct {
taskManager *runner.TaskManager
gradeRepo repository.GradeRepository
logger *zap.Logger
}
func NewGradeSyncService(
taskManager *runner.TaskManager,
gradeRepo repository.GradeRepository,
logger *zap.Logger,
) GradeSyncService {
return &gradeSyncService{
taskManager: taskManager,
gradeRepo: gradeRepo,
logger: logger,
}
}
func (s *gradeSyncService) SyncUserGrades(ctx context.Context, userID string, req *SyncGradesRequest) (*SyncGradesResult, error) {
payload := &pb.GetGradesPayload{
Username: req.Username,
Password: req.Password,
Semester: req.Semester,
}
taskResult, err := s.taskManager.SubmitTaskSync(ctx, pb.TaskType_TASK_TYPE_GET_GRADES, payload)
if err != nil {
s.logger.Error("failed to submit grades 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 &SyncGradesResult{
Success: false,
Message: "成绩获取失败",
ErrorMessage: taskResult.ErrorMessage,
}, nil
}
var resultData pb.GradesResultData
if err := json.Unmarshal(taskResult.Data, &resultData); err != nil {
s.logger.Error("failed to parse grades result",
zap.String("user_id", userID),
zap.Error(err))
return nil, fmt.Errorf("failed to parse result: %w", err)
}
grades := s.convertGrades(userID, &resultData)
if err := s.saveGrades(ctx, userID, grades); err != nil {
s.logger.Error("failed to save grades",
zap.String("user_id", userID),
zap.Error(err))
return nil, fmt.Errorf("failed to save grades: %w", err)
}
if resultData.GpaSummary != nil {
if err := s.gradeRepo.DeleteGpaSummaryByUser(ctx, userID); err != nil {
s.logger.Warn("failed to delete old gpa summary",
zap.String("user_id", userID),
zap.Error(err))
}
summary := s.convertGpaSummary(userID, resultData.GpaSummary)
if err := s.gradeRepo.SaveGpaSummary(ctx, summary); err != nil {
s.logger.Warn("failed to save gpa summary",
zap.String("user_id", userID),
zap.Error(err))
}
}
s.logger.Info("grades sync completed",
zap.String("user_id", userID),
zap.Int("grade_count", len(grades)))
return &SyncGradesResult{
Success: true,
Message: "成绩同步成功",
SyncedCount: len(grades),
}, nil
}
func (s *gradeSyncService) convertGrades(userID string, data *pb.GradesResultData) []*model.Grade {
grades := make([]*model.Grade, 0, len(data.Grades))
for _, g := range data.Grades {
grades = append(grades, &model.Grade{
ID: uuid.New().String(),
UserID: userID,
Term: g.Term,
CourseCode: g.CourseCode,
CourseName: g.CourseName,
Nature: g.Nature,
Category: g.Category,
Credit: g.Credit,
Score: g.Score,
GradePoint: g.GradePoint,
IsExam: g.IsExam,
})
}
return grades
}
func (s *gradeSyncService) convertGpaSummary(userID string, gpa *pb.GpaSummary) *model.GpaSummary {
return &model.GpaSummary{
ID: uuid.New().String(),
UserID: userID,
Semester: gpa.Semesters,
AverageGpa: gpa.AverageGpa,
Rank: gpa.Rank,
ExamTotalGpa: gpa.ExamTotalGpa,
ExamTotalCredits: gpa.ExamTotalCredits,
FailCredits: gpa.FailCredits,
Semesters: gpa.Semesters,
}
}
func (s *gradeSyncService) saveGrades(ctx context.Context, userID string, grades []*model.Grade) error {
if err := s.gradeRepo.DeleteByUser(ctx, userID); err != nil {
return fmt.Errorf("failed to delete old grades: %w", err)
}
if len(grades) == 0 {
return nil
}
if err := s.gradeRepo.BatchCreate(ctx, grades); err != nil {
return fmt.Errorf("failed to create grades: %w", err)
}
return nil
}