package com.qxgmat.service.extend; import com.alibaba.fastjson.JSONObject; import com.github.pagehelper.Page; import com.nuliji.tools.AbstractService; import com.nuliji.tools.PageResult; import com.nuliji.tools.Transform; import com.nuliji.tools.exception.ParameterException; import com.nuliji.tools.exception.SystemException; import com.qxgmat.data.constants.enums.QuestionDifficult; import com.qxgmat.data.constants.enums.QuestionSubject; import com.qxgmat.data.constants.enums.QuestionType; import com.qxgmat.data.constants.enums.ServiceKey; import com.qxgmat.data.constants.enums.module.PaperOrigin; import com.qxgmat.data.constants.enums.status.DirectionStatus; import com.qxgmat.data.dao.entity.*; import com.qxgmat.data.relation.ExaminationPaperRelationMapper; import com.qxgmat.data.relation.QuestionNoRelationMapper; import com.qxgmat.data.relation.UserPaperRelationMapper; import com.qxgmat.data.relation.UserReportRelationMapper; import com.qxgmat.data.relation.entity.QuestionDifficultRelation; import com.qxgmat.data.relation.entity.QuestionNoRelation; import com.qxgmat.service.UserPaperService; import com.qxgmat.service.UsersService; import com.qxgmat.service.inline.*; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.*; @Service public class ExaminationService extends AbstractService { private static Random rand = new Random(); @Resource private QuestionService questionService; @Resource private QuestionNoService questionNoService; @Resource private QuestionNoRelationMapper questionNoRelationMapper; @Resource private ExaminationStructService examinationStructService; @Resource private ExaminationPaperService examinationPaperService; @Resource private UserPaperRelationMapper userPaperRelationMapper; @Resource private UserPaperService userPaperService; @Resource private UserReportRelationMapper userReportRelationMapper; @Resource private UserReportService userReportService; @Resource private UsersService usersService; @Resource private ExaminationPaperRelationMapper examinationPaperRelationMapper; /** * verbal考试相关设置 */ final public Integer verbalMaxLevel = 27; final public Integer verbalMinLevel = 9; final public Integer verbalInitLevel = 13; final public Integer verbalPre = 9; final public Integer verbalSC = 14; final public Integer verbalCR = 9; final public Integer verbalRC = 13; // 出RC阅读题的题号 final public Integer[] verbalRCPosition = new Integer[]{5, 15, 24, 33}; /** * quant考试相关设置 */ final public Integer[] quantBaseLevel = new Integer[]{8, 8, 8, 7}; // 阶段的最大题目数 final public Integer[] quantNumber = new Integer[]{0, 8, 16, 24, 31}; final public Integer quantMinRatio = 1; final public Integer quantMaxRatio = 3; final public Integer quantInitLevel = 11; final public Integer quantPS = 17; final public Integer quantDS = 14; /** * 根据第三层结构建立paper * @param entity * @return */ @Transactional public ExaminationStruct addPaper(ExaminationStruct entity){ entity = examinationStructService.add(entity); if (entity.getLevel() == 3){ // 添加2份paper ExaminationPaper paper = examinationPaperService.add(ExaminationPaper.builder() .isAdapt(entity.getIsAdapt()) .structTwo(entity.getParentId()) .structThree(entity.getId()) .title(entity.getTitleZh()) .build() ); } return entity; } @Transactional public ExaminationStruct editPaper(ExaminationStruct entity){ entity = examinationStructService.add(entity); if (entity.getLevel() == 3){ ExaminationPaper paper = examinationPaperService.getByThree(entity.getId()); if(paper == null){ paper = examinationPaperService.add(ExaminationPaper.builder() .isAdapt(entity.getIsAdapt()) .structTwo(entity.getParentId()) .structThree(entity.getId()) .title(entity.getTitleZh()) .build() ); }else{ examinationPaperService.edit(ExaminationPaper.builder() .id(paper.getId()) .isAdapt(entity.getIsAdapt()) .structTwo(entity.getParentId()) .structThree(entity.getId()) .title(entity.getTitleZh()) .build() ); } } return entity; } @Transactional public Boolean deletePaper(Integer id){ Boolean result = examinationStructService.delete(id); ExaminationPaper paper = examinationPaperService.getByThree(id); if(paper != null){ examinationPaperService.delete(paper.getId()); } return result; } /** * * @param page * @param pageSize * @param questionType * @param structId * @param questionNo * @param paperId * @param place * @param difficult * @param order * @param direction * @return */ public Page listAdmin(int page, int pageSize, String questionType, Number structId, Number questionNo, Number paperId, String place, String difficult, String order, DirectionStatus direction){ if(order == null || order.isEmpty()) order = "id"; if (direction == null){ direction = DirectionStatus.DESC; } String finalOrder = order; DirectionStatus finalDirection = direction; Page p = page(() -> { questionNoRelationMapper.listExaminationAdmin(questionType, structId, questionNo, paperId, place, difficult, finalOrder, finalDirection.key); }, page, pageSize); Collection ids = Transform.getIds(p, QuestionNoRelation.class, "id"); // 获取详细数据 List list = questionNoService.relation(questionNoService.select(ids)); Transform.replace(p, list, QuestionNoRelation.class, "id"); return p; } /** * 查找模考组卷 * @param page * @param size * @param structId * @param userId * @param times * @return */ public PageResult list(int page, int size, Number structId, Number userId, Integer qxCatNo, Integer times){ Page p = page(()->{ examinationPaperRelationMapper.listWithUser(structId, userId, qxCatNo, times); },page, size); Collection ids = Transform.getIds(p, ExaminationPaper.class, "id"); // 获取详细数据 List list = examinationPaperService.select(ids); return new PageResult<>(list, p.getTotal()); } /** * cat模考是否已经完成 * @param userId * @return */ public boolean isFinishCat(Integer userId){ ExaminationStruct struct = getCat(); List paperList = examinationPaperService.listByTwo(struct.getId()); Collection ids = Transform.getIds(paperList, ExaminationPaper.class, "id"); List userPaperList = userPaperService.listWithOrigin(userId, PaperOrigin.EXAMINATION, ids, null); if (paperList.size() != userPaperList.size()){ return false; } Collection paperIds = Transform.getIds(userPaperList, UserPaper.class, "id"); List reportList = userReportService.listWithLater(paperIds); for(UserReport report: reportList){ if(report.getIsFinish() == 0){ return false; } } return true; } /** * 重置Cat模考系列 * @param userId * @return */ @Transactional public Boolean resetCat(Integer userId, boolean force){ ExaminationStruct struct = getCat(); List paperList = examinationPaperService.listByTwo(struct.getId()); Collection ids = Transform.getIds(paperList, ExaminationPaper.class, "id"); List userPaperList = userPaperService.listWithOrigin(userId, PaperOrigin.EXAMINATION, ids, null); if (!force && paperList.size() != userPaperList.size()){ throw new ParameterException("未完成所有"); } Collection paperIds = Transform.getIds(userPaperList, UserPaper.class, "id"); if(!force){ List reportList = userReportService.listWithLater(paperIds); for(UserReport report: reportList){ if(report.getIsFinish() == 0){ throw new ParameterException("未完成所有"); } } } // 增加用户cat计数 User user = usersService.get(userId); usersService.edit(User.builder().id(userId).qxCat(user.getQxCat() + 1).build()); return userPaperService.reset(paperIds, userId); } public ExaminationStruct getCat(){ List list = examinationStructService.main(); for (ExaminationStruct struct : list){ if (struct.getLevel() == 1) continue; if (struct.getExtend().equals(ServiceKey.QX_CAT.key)) return struct; } throw new SystemException("没有找到cat模考节点"); } /** * 获取下一阶段难度分 * @param currentLevel * @param number * @param correct * @return */ public Integer verbalNextLevel(Integer currentLevel, Integer number, long correct){ long correctRate = correct * 100 / number; if (correctRate < 60){ currentLevel -= 5; if (currentLevel < verbalMinLevel) return verbalMinLevel; }else if(correctRate > 80){ currentLevel += 5; if (currentLevel > verbalMaxLevel) return verbalMaxLevel; } return currentLevel; } /** * 获取下一阶段难度分 * @param currentLevel * @param number * @param correct * @param step * @return */ public Integer quantNextLevel(Integer currentLevel, Integer number, long correct, Integer step){ long correctRate = correct * 100 / number; Integer max = quantMaxRatio * quantBaseLevel[step]; Integer min = quantMinRatio * quantBaseLevel[step]; if (correctRate < 60){ currentLevel -= 4; if (currentLevel < min) return min; }else if(correctRate > 80){ currentLevel += 4; if (currentLevel > max) return max; } return currentLevel; } /** * 分组统计不同题型的数量 * @param userQuestionList * @return */ public Map statTypeNumber(List userQuestionList){ Map result = new HashMap<>(); for(UserQuestion question : userQuestionList){ String type = question.getQuestionType(); if (!result.containsKey(type)){ result.put(type, 0); } result.put(type, result.get(type) + 1); } return result; } /** * 返回还需的题目类型 * @param typeNumber * @param questionTypes * @return */ public List needQuestionTypes(Map typeNumber, List questionTypes){ List targetTypes = new ArrayList<>(); for(String type : questionTypes){ int target = 0; switch(type){ case "sc": target = verbalSC; break; case "cr": target = verbalCR; break; case "rc": target = verbalRC; break; case "ps": target = quantPS; break; case "ds": target = quantDS; break; } if (target == 0 || target > typeNumber.get(type)){ targetTypes.add(type); } } return targetTypes; } /** * 通过当前完成的题目数,判断是否该抽取阅读题 * @param number * @return */ public boolean verbalRC(Integer number){ for(Integer n : verbalRCPosition){ if (n == number) return true; } return false; } /** * 通过当前完成的题目数,判断当前阶段 * @param number * @return */ public Integer quantStep(Integer number) { Integer step = 0; Integer all = 0; for(Integer n : quantBaseLevel){ all += n; if (all >= number) return step; step += 1; } return step; } /** * 初始化第一阶段题目 * 一篇4题的阅读压轴 * { "ids": [], "level": 0 } * @param structId * @return */ public JSONObject initVerbal(Integer structId){ JSONObject info = new JSONObject(); Integer[] rcQ = questionNoService.randomExaminationRc(structId, 4, null); List rcRelationList = questionNoService.listWithRelationByIds(rcQ); Integer rcLevel = computeNoLevel(rcRelationList); Integer targetLevel = 0; Collection types = QuestionType.FromSubject(QuestionSubject.VERBAL); List difficultList = questionNoService.allExaminationByType(structId, types); List selectedList = null; do{ selectedList = randomList(difficultList, verbalPre - 4); targetLevel = rcLevel + computeDifficultLevel(selectedList); }while(targetLevel >= verbalInitLevel); List targetIds = new ArrayList<>(verbalPre); for(QuestionDifficultRelation relation : selectedList){ targetIds.add(relation.getId()); } targetIds.addAll(Arrays.asList(rcQ)); info.put("level", targetLevel); info.put("ids", targetIds); return info; } /** * 生成下一阶段题目 * 一篇3题的阅读压轴 * { "ids": [], "level": 0 } * @param structId * @param level * @param typeNumbers * @param ids * @return */ public JSONObject generateVerbal(Integer structId, Integer level, Map typeNumbers, Collection ids){ JSONObject info = new JSONObject(); Integer[] rcQ = questionNoService.randomExaminationRc(structId, 3, ids); List rcRelationList = questionNoService.listWithRelationByIds(rcQ); Integer rcLevel = computeNoLevel(rcRelationList); Collection types = QuestionType.FromSubject(QuestionSubject.VERBAL); List difficultList = questionNoService.allExaminationByType(structId, types); List selectedList = new ArrayList<>(verbalPre - 3); Integer[] levels = generateLevel(level - rcLevel,verbalPre - 3); do{ QuestionDifficultRelation r = random(difficultList, ids, levels[selectedList.size()]); int typeNumber = typeNumbers.get(r.getQuestionType()) + 1; switch(r.getQuestionType()){ case "sc": if (typeNumber > verbalSC) { continue; } break; case "cr": if (typeNumber > verbalCR) { continue; } break; } typeNumbers.put(r.getQuestionType(), typeNumber); selectedList.add(r); ids.add(r.getId()); }while(selectedList.size() == verbalPre -3); List targetIds = new ArrayList<>(verbalPre); for(QuestionDifficultRelation relation : selectedList){ targetIds.add(relation.getId()); } targetIds.addAll(Arrays.asList(rcQ)); info.put("ids", targetIds); info.put("level", level); return info; } /** * 初始化第一阶段题目 * 一篇4题的阅读压轴 * { "ids": [], "level": 0 } * @param structId * @return */ public JSONObject initQuant(Integer structId){ JSONObject info = new JSONObject(); Integer targetLevel = 0; Collection types = QuestionType.FromSubject(QuestionSubject.QUANT); Integer number = quantBaseLevel[0]; List difficultList = questionNoService.allExaminationByType(structId, types); List selectedList = null; do{ selectedList = randomList(difficultList, number); targetLevel = computeDifficultLevel(selectedList); }while(targetLevel >= quantInitLevel); List targetIds = new ArrayList<>(number); for(QuestionDifficultRelation relation : selectedList){ targetIds.add(relation.getId()); } info.put("level", targetLevel); info.put("ids", targetIds); return info; } /** * 生成下一阶段题目 * 一篇3题的阅读压轴 * { "ids": [], "level": 0 } * @param structId * @param level * @param typeNumbers * @param ids * @return */ public JSONObject generateQuant(Integer structId, Integer level, Map typeNumbers, Collection ids, Integer step){ JSONObject info = new JSONObject(); Collection types = QuestionType.FromSubject(QuestionSubject.QUANT); Integer number = quantBaseLevel[step]; List difficultList = questionNoService.allExaminationByType(structId, types); List selectedList = null; Integer[] levels = generateLevel(level,number); do{ QuestionDifficultRelation r = random(difficultList, ids, levels[selectedList.size()]); int typeNumber = typeNumbers.get(r.getQuestionType()) + 1; switch(r.getQuestionType()){ case "ps": if (typeNumber > quantPS) { continue; } break; case "ds": if (typeNumber > quantDS) { continue; } break; } typeNumbers.put(r.getQuestionType(), typeNumber); selectedList.add(r); ids.add(r.getId()); }while(selectedList.size() == number); List targetIds = new ArrayList<>(number); for(QuestionDifficultRelation relation : selectedList){ targetIds.add(relation.getId()); } info.put("ids", targetIds); info.put("level", level); return info; } /** * 随机生成对应的题目列表 * @param all * @param size * @return */ public List randomList(List all, Integer size){ List list = new ArrayList<>(size); do{ int index = rand.nextInt(all.size()); QuestionDifficultRelation target = all.get(index); list.add(target); }while(list.size() == size); return list; } /** * 随机生成对应的题目 * @param all * @param ids * @param level * @return */ public QuestionDifficultRelation random(List all, Collection ids, Integer level){ String difficult = levelToDifficult(level); do{ int index = rand.nextInt(all.size()); QuestionDifficultRelation target = all.get(index); if (ids == null || !ids.contains(target.getId())){ if (difficult == null || target.getDifficult().equals(difficult)) return target; } }while(true); } /** * 计算考题对应的难度分 * @param relationList * @return */ public Integer computeNoLevel(List relationList){ int level = 0; for(QuestionNoRelation relation : relationList){ level += difficultToLevel(relation.getQuestion().getDifficult()); } return level; } public Integer computeDifficultLevel(List relationList){ int level = 0; for(QuestionDifficultRelation relation : relationList){ level += difficultToLevel(relation.getDifficult()); } return level; } public Integer difficultToLevel(String difficult){ switch(difficult){ case "easy": return 1; case "medium": return 2; case "hard": return 3; } return 0; } public String levelToDifficult(Integer level){ switch(level){ case 1: return "easy"; case 2: return "medium"; case 3: return "hard"; } return null; } public Integer[] generateLevel(Integer level, Integer number){ Integer[] levels = new Integer[number]; int no = 0; // 至少有一个可以3分 if (level > number + 1){ for(int i = 0; i < number; i++){ int n = rand.nextInt(2) + 2; // 随机2-3 levels[i] = n; level -= n; no += 1; // 只能还有一个2分 if (level == number - no + 1){ break; } } number -= no; } if(level == number + 1){ // 只有一个2分 levels[no] = 2; no += 1; } // 剩余都是1分 for (int i = 0; i < number; i++) { levels[i+no] = 1; } shuffle(levels); return levels; } public static void swap(T[] a, int i, int j){ T temp = a[i]; a[i] = a[j]; a[j] = temp; } public static void shuffle(T[] arr) { int length = arr.length; for ( int i = length; i > 0; i-- ){ int randInd = rand.nextInt(i); swap(arr, randInd, i - 1); } } }