Add new functionality for managing and synchronizing academic grades and exam schedules. This includes new models, repositories, services, and HTTP handlers, along with updated gRPC definitions and dependency injection configuration. Additionally, improve the reliability of unread message count tracking by implementing an atomic Lua script for Redis operations in the conversation cache.
131 lines
3.5 KiB
Go
131 lines
3.5 KiB
Go
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)
|
|
}
|
|
|
|
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
|
|
} |