|  | @@ -1,6 +1,7 @@
 | 
	
		
			
				|  |  |  package com.fs.course.service.impl;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import cn.hutool.json.JSONUtil;
 | 
	
		
			
				|  |  | +import com.alibaba.fastjson.JSON;
 | 
	
		
			
				|  |  |  import com.alibaba.fastjson.JSONArray;
 | 
	
		
			
				|  |  |  import com.alibaba.fastjson.JSONObject;
 | 
	
		
			
				|  |  |  import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 | 
	
	
		
			
				|  | @@ -10,7 +11,10 @@ import com.fs.common.utils.DateUtils;
 | 
	
		
			
				|  |  |  import com.fs.common.utils.StringUtils;
 | 
	
		
			
				|  |  |  import com.fs.course.cache.FsUserCourseCategoryCacheService;
 | 
	
		
			
				|  |  |  import com.fs.course.config.CourseConfig;
 | 
	
		
			
				|  |  | -import com.fs.course.domain.*;
 | 
	
		
			
				|  |  | +import com.fs.course.domain.FsCourseAnswerLogs;
 | 
	
		
			
				|  |  | +import com.fs.course.domain.FsCourseQuestionBank;
 | 
	
		
			
				|  |  | +import com.fs.course.domain.FsCourseWatchLog;
 | 
	
		
			
				|  |  | +import com.fs.course.domain.FsUserCourseCategory;
 | 
	
		
			
				|  |  |  import com.fs.course.dto.FsCourseQuestionBankImportDTO;
 | 
	
		
			
				|  |  |  import com.fs.course.mapper.*;
 | 
	
		
			
				|  |  |  import com.fs.course.param.FsCourseQuestionAnswerUParam;
 | 
	
	
		
			
				|  | @@ -21,15 +25,15 @@ import com.fs.store.mapper.FsUserMapper;
 | 
	
		
			
				|  |  |  import com.fs.store.service.IFsStorePaymentService;
 | 
	
		
			
				|  |  |  import com.fs.system.service.ISysConfigService;
 | 
	
		
			
				|  |  |  import com.google.gson.JsonParser;
 | 
	
		
			
				|  |  | -import com.hc.openapi.tool.fastjson.JSON;
 | 
	
		
			
				|  |  |  import jodd.util.StringUtil;
 | 
	
		
			
				|  |  | -import org.apache.commons.collections4.CollectionUtils;
 | 
	
		
			
				|  |  | +import lombok.Getter;
 | 
	
		
			
				|  |  |  import org.springframework.beans.factory.annotation.Autowired;
 | 
	
		
			
				|  |  |  import org.springframework.stereotype.Service;
 | 
	
		
			
				|  |  |  import org.springframework.transaction.annotation.Transactional;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -import javax.validation.constraints.Size;
 | 
	
		
			
				|  |  |  import java.util.*;
 | 
	
		
			
				|  |  | +import java.util.function.BiConsumer;
 | 
	
		
			
				|  |  | +import java.util.function.Function;
 | 
	
		
			
				|  |  |  import java.util.stream.Collectors;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /**
 | 
	
	
		
			
				|  | @@ -360,134 +364,297 @@ public class FsCourseQuestionBankServiceImpl implements IFsCourseQuestionBankSer
 | 
	
		
			
				|  |  |       * @return String
 | 
	
		
			
				|  |  |       */
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  | -    public String importData(List<FsCourseQuestionBankImportDTO> list, @Size String nickName) {
 | 
	
		
			
				|  |  | +    public String importData(List<FsCourseQuestionBankImportDTO> list, String nickName) {
 | 
	
		
			
				|  |  |          if (Objects.isNull(list) || list.isEmpty()) {
 | 
	
		
			
				|  |  |              throw new ServiceException("导入数据不能为空");
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        int successNum = 0;
 | 
	
		
			
				|  |  | -        int failureNum = 0;
 | 
	
		
			
				|  |  | -        StringBuilder importSuccessMsg = new StringBuilder();
 | 
	
		
			
				|  |  | -        StringBuilder importErrorMsg = new StringBuilder();
 | 
	
		
			
				|  |  | -        StringBuilder importMsg = new StringBuilder();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +        ImportResult result = new ImportResult();
 | 
	
		
			
				|  |  |          List<FsCourseQuestionBank> importData = new ArrayList<>();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        Map<String,FsUserCourseCategory> categoryData = courseCategoryMapper.queryAllCategoryData();
 | 
	
		
			
				|  |  | +        Map<String, FsUserCourseCategory> categoryData = courseCategoryMapper.queryAllCategoryData();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          for (FsCourseQuestionBankImportDTO importDTO : list) {
 | 
	
		
			
				|  |  |              try {
 | 
	
		
			
				|  |  | -                String title = importDTO.getTitle();
 | 
	
		
			
				|  |  | -                String type = importDTO.getType();
 | 
	
		
			
				|  |  | -                String questionType = importDTO.getQuestionType();
 | 
	
		
			
				|  |  | -                String questionSubTyp = importDTO.getQuestionSubTyp();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                String question = importDTO.getQuestion();
 | 
	
		
			
				|  |  | -                String answer = importDTO.getAnswer();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                if (StringUtils.isBlank(title) || StringUtils.isBlank(type) || StringUtils.isBlank(question) || StringUtils.isBlank(answer)) {
 | 
	
		
			
				|  |  | -                    String msg = "<br/>" + failureNum + "、题目 " + title + " 导入失败:信息不完整";
 | 
	
		
			
				|  |  | -                    importErrorMsg.append(msg);
 | 
	
		
			
				|  |  | -                    failureNum++;
 | 
	
		
			
				|  |  | +                // 数据验证
 | 
	
		
			
				|  |  | +                ValidationResult validation = validateImportData(importDTO);
 | 
	
		
			
				|  |  | +                if (!validation.isValid()) {
 | 
	
		
			
				|  |  | +                    result.addFailure(importDTO.getTitle(), validation.getErrorMessage());
 | 
	
		
			
				|  |  |                      continue;
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                if (!type.contains("单") && !type.contains("多")) {
 | 
	
		
			
				|  |  | -                    String msg = "<br/>" + failureNum + "、题目 " + title + " 导入失败:题目类型不正确";
 | 
	
		
			
				|  |  | -                    importErrorMsg.append(msg);
 | 
	
		
			
				|  |  | -                    failureNum++;
 | 
	
		
			
				|  |  | -                    continue;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | +                // 构建题目对象
 | 
	
		
			
				|  |  | +                FsCourseQuestionBank questionBank = buildQuestionBank(importDTO, categoryData, nickName);
 | 
	
		
			
				|  |  | +                importData.add(questionBank);
 | 
	
		
			
				|  |  | +                result.addSuccess(importDTO.getTitle());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                // 判断答案是否在选项里
 | 
	
		
			
				|  |  | -                String[] questions = question.split("\\|");
 | 
	
		
			
				|  |  | -                String[] answers = answer.split("\\|");
 | 
	
		
			
				|  |  | +            } catch (Exception e) {
 | 
	
		
			
				|  |  | +                result.addFailure(importDTO.getTitle(), "导入异常: " + e.getMessage());
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                if (type.contains("多") && answers.length < 2) {
 | 
	
		
			
				|  |  | -                    String msg = "<br/>" + failureNum + "、题目 " + title + " 导入失败:多选题答案不能少于2个";
 | 
	
		
			
				|  |  | -                    importErrorMsg.append(msg);
 | 
	
		
			
				|  |  | -                    failureNum++;
 | 
	
		
			
				|  |  | -                    continue;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | +        // 批量保存
 | 
	
		
			
				|  |  | +        if (!importData.isEmpty()) {
 | 
	
		
			
				|  |  | +            fsCourseQuestionBankMapper.insertFsCourseQuestionBankBatch(importData);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                List<String> optionsList = Arrays.asList(questions);
 | 
	
		
			
				|  |  | -                boolean allAnswersInOptions = true;
 | 
	
		
			
				|  |  | +        return result.buildResultMessage();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                // 遍历每一个正确答案文本
 | 
	
		
			
				|  |  | -                for (String correctAnswer : answers) {
 | 
	
		
			
				|  |  | -                    // 检查当前正确答案文本是否存在于选项列表中
 | 
	
		
			
				|  |  | -                    if (!optionsList.contains(correctAnswer.trim())) {
 | 
	
		
			
				|  |  | -                        allAnswersInOptions = false; // 发现一个不在选项里的答案
 | 
	
		
			
				|  |  | -                        break;
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 验证导入数据
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private ValidationResult validateImportData(FsCourseQuestionBankImportDTO importDTO) {
 | 
	
		
			
				|  |  | +        // 基础字段验证
 | 
	
		
			
				|  |  | +        if (StringUtils.isBlank(importDTO.getTitle())) {
 | 
	
		
			
				|  |  | +            return ValidationResult.fail("标题不能为空");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if (StringUtils.isBlank(importDTO.getType())) {
 | 
	
		
			
				|  |  | +            return ValidationResult.fail("类别不能为空");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if (Objects.isNull(importDTO.getSort())) {
 | 
	
		
			
				|  |  | +            return ValidationResult.fail("排序不能为空");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if (StringUtils.isBlank(importDTO.getAnswer())) {
 | 
	
		
			
				|  |  | +            return ValidationResult.fail("答案不能为空");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                if (!allAnswersInOptions) {
 | 
	
		
			
				|  |  | -                    String msg = "<br/>" + failureNum + "、题目 " + title + " 导入失败:答案不在选项中";
 | 
	
		
			
				|  |  | -                    importErrorMsg.append(msg);
 | 
	
		
			
				|  |  | -                    failureNum++;
 | 
	
		
			
				|  |  | -                    continue;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | +        // 选项验证
 | 
	
		
			
				|  |  | +        ValidationResult optionValidation = validateOptions(importDTO);
 | 
	
		
			
				|  |  | +        if (!optionValidation.isValid()) {
 | 
	
		
			
				|  |  | +            return optionValidation;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                JSONArray questionArray = new JSONArray();
 | 
	
		
			
				|  |  | -                optionsList = Arrays.asList(answers);
 | 
	
		
			
				|  |  | -                for (int i = 0; i < questions.length; i++) {
 | 
	
		
			
				|  |  | -                    JSONObject optionObj = new JSONObject();
 | 
	
		
			
				|  |  | -                    optionObj.put("name", questions[i]);
 | 
	
		
			
				|  |  | -                    optionObj.put("isAnswer", optionsList.contains(questions[i].trim()) ? 1 : 0);
 | 
	
		
			
				|  |  | -                    optionObj.put("indexId", i);
 | 
	
		
			
				|  |  | -                    questionArray.add(optionObj);
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | +        // 题目类型验证
 | 
	
		
			
				|  |  | +        if (!isValidQuestionType(importDTO.getType())) {
 | 
	
		
			
				|  |  | +            return ValidationResult.fail("题目类型不正确,仅支持单选题和多选题");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                // 分类
 | 
	
		
			
				|  |  | -                FsUserCourseCategory fsUserCourseCategory = categoryData.get(questionType);
 | 
	
		
			
				|  |  | -                Long questionTypeId = null;
 | 
	
		
			
				|  |  | -                if(fsUserCourseCategory != null) {
 | 
	
		
			
				|  |  | -                    questionTypeId = fsUserCourseCategory.getCateId();
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | +        // 答案验证
 | 
	
		
			
				|  |  | +        return validateAnswers(importDTO);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                // 题目子分类
 | 
	
		
			
				|  |  | -                fsUserCourseCategory = categoryData.get(questionSubTyp);
 | 
	
		
			
				|  |  | -                Long questionSubTypeId = null;
 | 
	
		
			
				|  |  | -                if(fsUserCourseCategory != null) {
 | 
	
		
			
				|  |  | -                    questionSubTypeId = fsUserCourseCategory.getCateId();
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 验证选项
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private ValidationResult validateOptions(FsCourseQuestionBankImportDTO importDTO) {
 | 
	
		
			
				|  |  | +        if (StringUtils.isBlank(importDTO.getQuestionA()) ||
 | 
	
		
			
				|  |  | +                StringUtils.isBlank(importDTO.getQuestionB()) ||
 | 
	
		
			
				|  |  | +                StringUtils.isBlank(importDTO.getQuestionC())) {
 | 
	
		
			
				|  |  | +            return ValidationResult.fail("选项A、B、C不能为空");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        return ValidationResult.success();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                FsCourseQuestionBank questionBank = new FsCourseQuestionBank();
 | 
	
		
			
				|  |  | -                questionBank.setTitle(title);
 | 
	
		
			
				|  |  | -                questionBank.setType(type.contains("单") ? 1L : 2L);
 | 
	
		
			
				|  |  | -                questionBank.setQuestionType(questionTypeId);
 | 
	
		
			
				|  |  | -                questionBank.setStatus(1L);
 | 
	
		
			
				|  |  | -                questionBank.setSort(importDTO.getSort());
 | 
	
		
			
				|  |  | -                questionBank.setQuestion(JSON.toJSONString(questionArray));
 | 
	
		
			
				|  |  | -                questionBank.setAnswer(answers.length > 1 ? JSON.toJSONString(answers) : answer);
 | 
	
		
			
				|  |  | -                questionBank.setCreateTime(new Date());
 | 
	
		
			
				|  |  | -                questionBank.setCreateBy(nickName);
 | 
	
		
			
				|  |  | -                questionBank.setQuestionSubType(questionSubTypeId);
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 验证题目类型
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private boolean isValidQuestionType(String type) {
 | 
	
		
			
				|  |  | +        return type.contains("单") || type.contains("多");
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                importData.add(questionBank);
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 验证答案
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private ValidationResult validateAnswers(FsCourseQuestionBankImportDTO importDTO) {
 | 
	
		
			
				|  |  | +        String[] answers = importDTO.getAnswer().trim().toUpperCase().split("");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        // 多选题答案数量验证
 | 
	
		
			
				|  |  | +        if (importDTO.getType().contains("多") && answers.length < 2) {
 | 
	
		
			
				|  |  | +            return ValidationResult.fail("多选题答案不能少于2个");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                importSuccessMsg.append("<br/>").append(successNum).append("、题目 ").append(title).append(" 导入成功");
 | 
	
		
			
				|  |  | -                successNum++;
 | 
	
		
			
				|  |  | -            } catch (Exception e) {
 | 
	
		
			
				|  |  | -                String msg = "<br/>" + failureNum + "、题目 " + importDTO.getTitle() + " 导入异常:";
 | 
	
		
			
				|  |  | -                importErrorMsg.append(msg).append(e.getMessage());
 | 
	
		
			
				|  |  | -                failureNum++;
 | 
	
		
			
				|  |  | +        // 答案选项验证
 | 
	
		
			
				|  |  | +        List<String> validOptions = getValidOptions(importDTO);
 | 
	
		
			
				|  |  | +        for (String answer : answers) {
 | 
	
		
			
				|  |  | +            if (!validOptions.contains(answer.trim())) {
 | 
	
		
			
				|  |  | +                return ValidationResult.fail("答案不在选项中");
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if(CollectionUtils.isNotEmpty(importData)) {
 | 
	
		
			
				|  |  | -            fsCourseQuestionBankMapper.insertFsCourseQuestionBankBatch(importData);
 | 
	
		
			
				|  |  | +        return ValidationResult.success();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 获取有效选项列表
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private List<String> getValidOptions(FsCourseQuestionBankImportDTO importDTO) {
 | 
	
		
			
				|  |  | +        List<String> options = new ArrayList<>(Arrays.asList("A", "B", "C"));
 | 
	
		
			
				|  |  | +        if (StringUtils.isNotBlank(importDTO.getQuestionD())) {
 | 
	
		
			
				|  |  | +            options.add("D");
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | +        return options;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 构建题目对象
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private FsCourseQuestionBank buildQuestionBank(FsCourseQuestionBankImportDTO importDTO,
 | 
	
		
			
				|  |  | +                                                   Map<String, FsUserCourseCategory> categoryData,
 | 
	
		
			
				|  |  | +                                                   String nickName) {
 | 
	
		
			
				|  |  | +        FsCourseQuestionBank questionBank = new FsCourseQuestionBank();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 基础信息
 | 
	
		
			
				|  |  | +        questionBank.setTitle(importDTO.getTitle());
 | 
	
		
			
				|  |  | +        questionBank.setType(getQuestionTypeCode(importDTO.getType()));
 | 
	
		
			
				|  |  | +        questionBank.setSort(importDTO.getSort());
 | 
	
		
			
				|  |  | +        questionBank.setStatus(parseStatus(importDTO.getStatus()));
 | 
	
		
			
				|  |  | +        questionBank.setCreateTime(new Date());
 | 
	
		
			
				|  |  | +        questionBank.setCreateBy(nickName);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 分类信息
 | 
	
		
			
				|  |  | +        setQuestionCategories(questionBank, importDTO, categoryData);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 选项和答案
 | 
	
		
			
				|  |  | +        questionBank.setQuestion(buildQuestionOptions(importDTO));
 | 
	
		
			
				|  |  | +        questionBank.setAnswer(buildAnswer(importDTO));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        return questionBank;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // 在所有导入处理完成后,构建最终的导入结果消息
 | 
	
		
			
				|  |  | -        importMsg.insert(0, "导入完成!成功" + successNum + " 条,失败" + failureNum + "条。");
 | 
	
		
			
				|  |  | -        importMsg.append(importErrorMsg);
 | 
	
		
			
				|  |  | -        importMsg.append(importSuccessMsg);
 | 
	
		
			
				|  |  | -        return importMsg.toString();
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 获取题目类型编码
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private Long getQuestionTypeCode(String type) {
 | 
	
		
			
				|  |  | +        return type.contains("单") ? 1L : 2L;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 解析状态
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private Long parseStatus(String statusStr) {
 | 
	
		
			
				|  |  | +        if (StringUtils.isBlank(statusStr)) {
 | 
	
		
			
				|  |  | +            return 1L;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        try {
 | 
	
		
			
				|  |  | +            return statusStr.contains("正常") ? 1L : 0L;
 | 
	
		
			
				|  |  | +        } catch (Exception e) {
 | 
	
		
			
				|  |  | +            return 1L;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 设置题目分类
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private void setQuestionCategories(FsCourseQuestionBank questionBank,
 | 
	
		
			
				|  |  | +                                       FsCourseQuestionBankImportDTO importDTO,
 | 
	
		
			
				|  |  | +                                       Map<String, FsUserCourseCategory> categoryData) {
 | 
	
		
			
				|  |  | +        if (StringUtils.isBlank(importDTO.getQuestionType())) {
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        FsUserCourseCategory category = categoryData.get(importDTO.getQuestionType().trim());
 | 
	
		
			
				|  |  | +        if (category != null) {
 | 
	
		
			
				|  |  | +            questionBank.setQuestionType(category.getCateId());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // 子分类
 | 
	
		
			
				|  |  | +            if (StringUtils.isNotBlank(importDTO.getQuestionSubTyp())) {
 | 
	
		
			
				|  |  | +                FsUserCourseCategory subCategory = categoryData.get(importDTO.getQuestionSubTyp().trim());
 | 
	
		
			
				|  |  | +                if (subCategory != null && Objects.equals(subCategory.getPid(), category.getCateId())) {
 | 
	
		
			
				|  |  | +                    questionBank.setQuestionSubType(subCategory.getCateId());
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 构建选项JSON
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private String buildQuestionOptions(FsCourseQuestionBankImportDTO importDTO) {
 | 
	
		
			
				|  |  | +        JSONArray questionArray = new JSONArray();
 | 
	
		
			
				|  |  | +        String[] answers = importDTO.getAnswer().trim().toUpperCase().split("");
 | 
	
		
			
				|  |  | +        List<String> answerList = Arrays.asList(answers);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 构建选项
 | 
	
		
			
				|  |  | +        addOption(questionArray, importDTO.getQuestionA(), answerList.contains("A"), 0);
 | 
	
		
			
				|  |  | +        addOption(questionArray, importDTO.getQuestionB(), answerList.contains("B"), 1);
 | 
	
		
			
				|  |  | +        addOption(questionArray, importDTO.getQuestionC(), answerList.contains("C"), 2);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if (StringUtils.isNotBlank(importDTO.getQuestionD())) {
 | 
	
		
			
				|  |  | +            addOption(questionArray, importDTO.getQuestionD(), answerList.contains("D"), 3);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        return JSON.toJSONString(questionArray);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 添加选项
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private void addOption(JSONArray questionArray, String optionText,
 | 
	
		
			
				|  |  | +                           boolean isAnswer, int indexId) {
 | 
	
		
			
				|  |  | +        JSONObject option = new JSONObject();
 | 
	
		
			
				|  |  | +        option.put("name", optionText.trim());
 | 
	
		
			
				|  |  | +        option.put("isAnswer", isAnswer ? 1 : 0);
 | 
	
		
			
				|  |  | +        option.put("indexId", indexId);
 | 
	
		
			
				|  |  | +        questionArray.add(option);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 构建答案
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private String buildAnswer(FsCourseQuestionBankImportDTO importDTO) {
 | 
	
		
			
				|  |  | +        String[] answers = importDTO.getAnswer().trim().toUpperCase().split("");
 | 
	
		
			
				|  |  | +        List<String> answerList = Arrays.asList(answers);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Map<String, String> questionMap = new HashMap<>();
 | 
	
		
			
				|  |  | +        questionMap.put("A", importDTO.getQuestionA().trim());
 | 
	
		
			
				|  |  | +        questionMap.put("B", importDTO.getQuestionB().trim());
 | 
	
		
			
				|  |  | +        questionMap.put("C", importDTO.getQuestionC().trim());
 | 
	
		
			
				|  |  | +        questionMap.put("D", importDTO.getQuestionD().trim());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        List<String> selectedAnswers = answerList.stream()
 | 
	
		
			
				|  |  | +                .map(questionMap::get)
 | 
	
		
			
				|  |  | +                .collect(Collectors.toList());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if (answerList.size() == 1) {
 | 
	
		
			
				|  |  | +            return selectedAnswers.get(0);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        return JSON.toJSONString(selectedAnswers);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // 辅助类
 | 
	
		
			
				|  |  | +    @Getter
 | 
	
		
			
				|  |  | +    private static class ValidationResult {
 | 
	
		
			
				|  |  | +        private final boolean valid;
 | 
	
		
			
				|  |  | +        private final String errorMessage;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private ValidationResult(boolean valid, String errorMessage) {
 | 
	
		
			
				|  |  | +            this.valid = valid;
 | 
	
		
			
				|  |  | +            this.errorMessage = errorMessage;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public static ValidationResult success() {
 | 
	
		
			
				|  |  | +            return new ValidationResult(true, null);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public static ValidationResult fail(String message) {
 | 
	
		
			
				|  |  | +            return new ValidationResult(false, message);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private static class ImportResult {
 | 
	
		
			
				|  |  | +        private int successNum = 0;
 | 
	
		
			
				|  |  | +        private int failureNum = 0;
 | 
	
		
			
				|  |  | +        private final StringBuilder successMsg = new StringBuilder();
 | 
	
		
			
				|  |  | +        private final StringBuilder errorMsg = new StringBuilder();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public void addSuccess(String title) {
 | 
	
		
			
				|  |  | +            successNum++;
 | 
	
		
			
				|  |  | +            successMsg.append("<br/>").append(successNum).append("、题目 ").append(title).append(" 导入成功");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public void addFailure(String title, String error) {
 | 
	
		
			
				|  |  | +            failureNum++;
 | 
	
		
			
				|  |  | +            errorMsg.append("<br/>").append(failureNum).append("、题目 ").append(title).append(" 导入失败:").append(error);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public String buildResultMessage() {
 | 
	
		
			
				|  |  | +            return "导入完成!成功" + successNum + " 条,失败" + failureNum + "条。" + errorMsg + successMsg;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      /**
 | 
	
		
			
				|  |  |       * 根据ID查询题目
 | 
	
		
			
				|  |  |       * @param ids   ids
 | 
	
	
		
			
				|  | @@ -554,6 +721,61 @@ public class FsCourseQuestionBankServiceImpl implements IFsCourseQuestionBankSer
 | 
	
		
			
				|  |  |          return fsCourseQuestionBankMapper.selectNameByCateId(cateId);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 题目导出
 | 
	
		
			
				|  |  | +     * @param fsCourseQuestionBank  参数
 | 
	
		
			
				|  |  | +     * @return  list
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    public List<FsCourseQuestionBankImportDTO> exportData(FsCourseQuestionBank fsCourseQuestionBank) {
 | 
	
		
			
				|  |  | +        List<FsCourseQuestionBank> fsCourseQuestionBanks = fsCourseQuestionBankMapper.selectFsCourseQuestionBankList(fsCourseQuestionBank);
 | 
	
		
			
				|  |  | +        Map<Long, FsUserCourseCategory> categoryMap = courseCategoryMapper.queryAllIdKeyCategoryData();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 辅助方法:根据ID从Map获取分类名称
 | 
	
		
			
				|  |  | +        Function<Long, String> getCategoryName = id -> Optional.ofNullable(categoryMap.get(id))
 | 
	
		
			
				|  |  | +                .map(FsUserCourseCategory::getCateName)
 | 
	
		
			
				|  |  | +                .orElse(null);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Map<Integer, BiConsumer<FsCourseQuestionBankImportDTO, String>> questionSetters = new HashMap<>();
 | 
	
		
			
				|  |  | +        questionSetters.put(0, FsCourseQuestionBankImportDTO::setQuestionA);
 | 
	
		
			
				|  |  | +        questionSetters.put(1, FsCourseQuestionBankImportDTO::setQuestionB);
 | 
	
		
			
				|  |  | +        questionSetters.put(2, FsCourseQuestionBankImportDTO::setQuestionC);
 | 
	
		
			
				|  |  | +        questionSetters.put(3, FsCourseQuestionBankImportDTO::setQuestionD);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Map<Integer, String> answerLettersMap = new HashMap<>();
 | 
	
		
			
				|  |  | +        answerLettersMap.put(0, "A");
 | 
	
		
			
				|  |  | +        answerLettersMap.put(1, "B");
 | 
	
		
			
				|  |  | +        answerLettersMap.put(2, "C");
 | 
	
		
			
				|  |  | +        answerLettersMap.put(3, "D");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        return fsCourseQuestionBanks.stream().map(q -> {
 | 
	
		
			
				|  |  | +            FsCourseQuestionBankImportDTO dto = new FsCourseQuestionBankImportDTO();
 | 
	
		
			
				|  |  | +            dto.setTitle(q.getTitle());
 | 
	
		
			
				|  |  | +            dto.setSort(q.getSort());
 | 
	
		
			
				|  |  | +            dto.setType(q.getType() == 1 ? "单选" : "多选");
 | 
	
		
			
				|  |  | +            Optional.ofNullable(q.getQuestionType()).map(getCategoryName).ifPresent(dto::setQuestionType);
 | 
	
		
			
				|  |  | +            Optional.ofNullable(q.getQuestionSubType()).map(getCategoryName).ifPresent(dto::setQuestionSubTyp);
 | 
	
		
			
				|  |  | +            dto.setStatus(q.getStatus() == 1 ? "正常" : "停用");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            StringBuilder answersBuilder = new StringBuilder();
 | 
	
		
			
				|  |  | +            JSONArray array = JSON.parseArray(q.getQuestion());
 | 
	
		
			
				|  |  | +            array.forEach(jsonObject -> {
 | 
	
		
			
				|  |  | +                JSONObject json = (JSONObject) jsonObject;
 | 
	
		
			
				|  |  | +                int index = json.getInteger("indexId");
 | 
	
		
			
				|  |  | +                String name = json.getString("name");
 | 
	
		
			
				|  |  | +                int isAnswer = json.getInteger("isAnswer");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                questionSetters.get(index).accept(dto, name);
 | 
	
		
			
				|  |  | +                if (isAnswer == 1) {
 | 
	
		
			
				|  |  | +                    answersBuilder.append(answerLettersMap.get(index));
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            });
 | 
	
		
			
				|  |  | +            dto.setAnswer(answersBuilder .toString());
 | 
	
		
			
				|  |  | +            return dto;
 | 
	
		
			
				|  |  | +        }).collect(Collectors.toList());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public static String[] convertStringToArray(String inputString) {
 | 
	
		
			
				|  |  |          String cleanString = inputString.replaceAll("[\\[\\]\"\\s]", "");
 |