package service import ( "fmt" "dd_fiber_api/internal/question" "dd_fiber_api/internal/question/dao" ) // PaperService 试卷服务 type PaperService struct { paperDAO dao.PaperDAOInterface materialService *MaterialService } // NewPaperService 创建试卷服务 func NewPaperService(paperDAO dao.PaperDAOInterface, materialService *MaterialService) *PaperService { return &PaperService{ paperDAO: paperDAO, materialService: materialService, } } // CreatePaper 创建试卷 func (s *PaperService) CreatePaper(req *CreatePaperRequest) (*question.Paper, error) { // 验证:题目数量不能为空 if req.QuestionIds == nil || len(req.QuestionIds) == 0 { return nil, fmt.Errorf("题目数量不能为空,请至少选择一个题目") } paperID := question.GenerateID() // 确保 MaterialIds 不为 nil materialIds := req.MaterialIds if materialIds == nil { materialIds = []string{} } // 确保 QuestionIDs 不为 nil(已验证不为空,但确保不为 nil) questionIds := req.QuestionIds if questionIds == nil { questionIds = []string{} } paper := &question.Paper{ ID: paperID, Title: req.Title, Description: req.Description, Source: req.Source, QuestionIDs: questionIds, MaterialIDs: materialIds, CreatedAt: question.GetCurrentTimestamp(), UpdatedAt: question.GetCurrentTimestamp(), } err := s.paperDAO.Create(paper) if err != nil { return nil, fmt.Errorf("创建试卷失败: %v", err) } return paper, nil } // GetPaper 获取试卷 func (s *PaperService) GetPaper(id string) (*question.Paper, error) { paper, err := s.paperDAO.GetWithQuestions(id) if err != nil { return nil, fmt.Errorf("试卷不存在: %v", err) } // 收集材料 ID:试卷级 + 题目级(按题目中首次出现顺序去重),并拉取材料详情 if s.materialService != nil { seen := make(map[string]bool) var orderedIDs []string for _, mid := range paper.MaterialIDs { if mid != "" && !seen[mid] { seen[mid] = true orderedIDs = append(orderedIDs, mid) } } if paper.Questions != nil { for _, q := range paper.Questions { if q.MaterialID != "" && !seen[q.MaterialID] { seen[q.MaterialID] = true orderedIDs = append(orderedIDs, q.MaterialID) } } } for _, mid := range orderedIDs { mat, err := s.materialService.GetMaterial(mid) if err != nil { continue } paper.Materials = append(paper.Materials, mat) } } return paper, nil } // SearchPapers 搜索试卷 func (s *PaperService) SearchPapers(query string, page, pageSize int32) ([]*question.Paper, int32, error) { papers, total, err := s.paperDAO.Search(query, page, pageSize) if err != nil { return nil, 0, fmt.Errorf("搜索试卷失败: %v", err) } return papers, total, nil } // UpdatePaper 更新试卷 func (s *PaperService) UpdatePaper(req *UpdatePaperRequest) error { // 验证:题目数量不能为空 if req.QuestionIds == nil || len(req.QuestionIds) == 0 { return fmt.Errorf("题目数量不能为空,请至少选择一个题目") } paper, err := s.paperDAO.GetByID(req.Id) if err != nil { return fmt.Errorf("试卷不存在: %v", err) } paper.Title = req.Title paper.Description = req.Description paper.Source = req.Source // 确保 QuestionIDs 不为 nil(已验证不为空,但确保不为 nil) questionIds := req.QuestionIds if questionIds == nil { questionIds = []string{} } paper.QuestionIDs = questionIds // 确保 MaterialIds 不为 nil materialIds := req.MaterialIds if materialIds == nil { materialIds = []string{} } paper.MaterialIDs = materialIds paper.UpdatedAt = question.GetCurrentTimestamp() err = s.paperDAO.Update(paper) if err != nil { return fmt.Errorf("更新试卷失败: %v", err) } return nil } // DeletePaper 删除试卷 func (s *PaperService) DeletePaper(id string) error { err := s.paperDAO.Delete(id) if err != nil { return fmt.Errorf("删除试卷失败: %v", err) } return nil } // BatchDeletePapers 批量删除试卷 func (s *PaperService) BatchDeletePapers(ids []string) (int, []string, error) { deletedCount, failedIDs, err := s.paperDAO.BatchDelete(ids) if err != nil { return 0, nil, fmt.Errorf("批量删除失败: %v", err) } return deletedCount, failedIDs, nil } // AddQuestionToPaper 添加题目到试卷 func (s *PaperService) AddQuestionToPaper(paperID string, questionIDs []string) (int, []string, error) { addedCount, failedIDs, err := s.paperDAO.AddQuestions(paperID, questionIDs) if err != nil { return 0, nil, fmt.Errorf("添加题目失败: %v", err) } return addedCount, failedIDs, nil } // RemoveQuestionFromPaper 从试卷移除题目 func (s *PaperService) RemoveQuestionFromPaper(paperID string, questionIDs []string) (int, []string, error) { removedCount, failedIDs, err := s.paperDAO.RemoveQuestions(paperID, questionIDs) if err != nil { return 0, nil, fmt.Errorf("移除题目失败: %v", err) } return removedCount, failedIDs, nil } // GetPaperWithAnswers 获取试卷及题目详情(包含答案和解析,用于答题结果页面) // 这个方法确保返回的题目包含答案和解析字段 func (s *PaperService) GetPaperWithAnswers(id string) (*question.Paper, error) { paper, err := s.GetPaper(id) if err != nil { return nil, err } // 确保每个题目都有答案和解析字段(即使为空字符串) if paper.Questions != nil { for _, q := range paper.Questions { if q.Answer == "" { q.Answer = "" } if q.Explanation == "" { q.Explanation = "" } } } return paper, nil } // CreatePaperRequest 创建试卷请求 type CreatePaperRequest struct { Title string `json:"title"` Description string `json:"description"` Source string `json:"source,omitempty"` // 题目出处(可选) QuestionIds []string `json:"question_ids"` // 题目ID列表 MaterialIds []string `json:"material_ids,omitempty"` // 关联的材料ID列表(可选,用于主观题组卷) } // UpdatePaperRequest 更新试卷请求 type UpdatePaperRequest struct { Id string `json:"id"` Title string `json:"title"` Description string `json:"description"` Source string `json:"source,omitempty"` // 题目出处(可选) QuestionIds []string `json:"question_ids"` // 题目ID列表 MaterialIds []string `json:"material_ids,omitempty"` // 关联的材料ID列表(可选,用于主观题组卷) }