RecommendServiceImpl.java 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. package com.seecoder.dataanalysis.analysis.impl;
  2. import cn.seecoder.bok.common.vo.QuestionVO;
  3. import com.seecoder.dataanalysis.analysis.EvalDataAnalysisService;
  4. import com.seecoder.dataanalysis.analysis.RecommendService;
  5. import com.seecoder.dataanalysis.data.dao.competency.CompetencyTreeDao;
  6. import com.seecoder.dataanalysis.data.entity.competency.CompetencyTree;
  7. import com.seecoder.dataanalysis.data.entity.competency.KSDScore;
  8. import com.seecoder.dataanalysis.logic.question.QuestionService;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.stereotype.Service;
  11. import java.util.*;
  12. @Service
  13. public class RecommendServiceImpl implements RecommendService {
  14. //默认推荐的题目数量
  15. public static final int QUESTION_NUM = 30;
  16. @Autowired
  17. private EvalDataAnalysisService evalDataAnalysisService;
  18. @Autowired
  19. private QuestionService questionService;
  20. @Autowired
  21. private CompetencyTreeDao competencyTreeDao;
  22. @Override
  23. public List<String> getRecommendById(int userId){
  24. Random random = new Random();
  25. //获取学生的做题情况
  26. Map<String,Double> errorQuestions = evalDataAnalysisService.getStudentErrorQuestions(userId);
  27. //学生的错题列表,只存储0分的题目
  28. //本次只考虑SR类型的错题,其他都不添加
  29. //TODO 针对多个知识点的情况
  30. String target = "SR";
  31. List<String> errors = new ArrayList<>();
  32. for(Map.Entry<String,Double> entry : errorQuestions.entrySet()){
  33. String questionId = entry.getKey();
  34. if(entry.getValue().equals(0.0)){
  35. List<String> ks = questionService.getKnowledgesById(questionId);
  36. for(String k : ks){
  37. if(k.contains(target)){
  38. errors.add(questionId);
  39. break;
  40. }
  41. }
  42. }
  43. }
  44. CompetencyTree competencyTree;
  45. //获取学生的能力树
  46. Optional<CompetencyTree> result = competencyTreeDao.findById(userId);
  47. if(!result.isPresent()){
  48. competencyTree = new CompetencyTree(userId);
  49. }else {
  50. competencyTree = competencyTreeDao.findById(userId).get();
  51. }
  52. //先推荐没做过的(没做过的类型再到同类型没做过的题目),再推荐分支中分数低的
  53. //本次题目都是SR类的,所以只查看SR分支
  54. List<String> tarKnows = new ArrayList<>();
  55. tarKnows.add("SR");
  56. //每种类型推荐多少题目,由知识点数目而定
  57. int eachNum = QUESTION_NUM / tarKnows.size();
  58. //知识点和对应分数
  59. Map<String,Double> knowlegdeAndScores = new HashMap<>();
  60. for(String tarKnow :tarKnows){
  61. String[] tars = tarKnow.split("\\.");
  62. List<KSDScore> ksdScoreList = competencyTree.getSubCompetencyTree();
  63. for(KSDScore ksdScore : ksdScoreList){
  64. //先匹配最外层
  65. if(ksdScore.getCompetency().equals(tars[0])){
  66. if(!knowlegdeAndScores.containsKey(tars[0])){
  67. knowlegdeAndScores.put(tars[0],ksdScore.getTotalScore());
  68. }
  69. //再匹配第二层
  70. //TODO 完善匹配机制
  71. if(tars.length>1){}
  72. //将分支下所有子分支都添加
  73. if(ksdScore.getChildren()!=null&&ksdScore.getChildren().size()>0){
  74. for(KSDScore subKSD1 : ksdScore.getChildren()){
  75. if(!knowlegdeAndScores.containsKey(subKSD1.getCompetency())){
  76. knowlegdeAndScores.put(subKSD1.getCompetency(),subKSD1.getTotalScore());
  77. }
  78. if(subKSD1.getChildren()!=null&&subKSD1.getChildren().size()>0){
  79. for(KSDScore subKSD2 : subKSD1.getChildren()){
  80. if(!knowlegdeAndScores.containsKey(subKSD2.getCompetency())){
  81. knowlegdeAndScores.put(subKSD2.getCompetency(),subKSD2.getTotalScore());
  82. }
  83. }
  84. }
  85. }
  86. }
  87. }
  88. }
  89. }
  90. // List<KSDScore> ksdScoreList = competencyTree.getSubCompetencyTree();
  91. // for(KSDScore ksdScore : ksdScoreList){
  92. // if(ksdScore.getCompetency().equals(target)){
  93. // if(!knowlegdeAndScores.containsKey(target)){
  94. // knowlegdeAndScores.put(target,ksdScore.getTotalScore());
  95. // }
  96. // //将分支下所有的子分支都添加
  97. // if(ksdScore.getChildren()!=null&&ksdScore.getChildren().size()>0){
  98. // for(KSDScore subKSD1 : ksdScore.getChildren()){
  99. // if(!knowlegdeAndScores.containsKey(subKSD1.getCompetency())){
  100. // knowlegdeAndScores.put(subKSD1.getCompetency(),subKSD1.getTotalScore());
  101. // }
  102. // if(subKSD1.getChildren()!=null&&subKSD1.getChildren().size()>0){
  103. // for(KSDScore subKSD2 : subKSD1.getChildren()){
  104. // if(!knowlegdeAndScores.containsKey(subKSD2.getCompetency())){
  105. // knowlegdeAndScores.put(subKSD2.getCompetency(),subKSD2.getTotalScore());
  106. // }
  107. // }
  108. // }
  109. // }
  110. // }
  111. // }
  112. // }
  113. List<Map.Entry<String,Double>> list = new ArrayList<>(knowlegdeAndScores.entrySet());
  114. Collections.sort(list,(a,b)->(int)((double)a.getValue()-(double)b.getValue()));
  115. //根据得分升序排列,得到学生相对薄弱的部分
  116. Map<String,Double> afterSort = new LinkedHashMap<>();
  117. for(Map.Entry<String,Double> o : list){
  118. afterSort.put(o.getKey(),o.getValue());
  119. }
  120. // for(Map.Entry<String,Double> entry : afterSort.entrySet()){
  121. // System.out.println(entry.getKey()+": "+entry.getValue());
  122. // }
  123. //还需要添加的题目数目,姑且设计为5错题+5错题同类型题+15低分能力下的题目+5大类中未做过的,剩下不足的由未做过的题目补充
  124. int left = QUESTION_NUM;
  125. //最终生成试卷的所有题号的集合
  126. List<String> total = new ArrayList<>();
  127. //随机选择5道错题
  128. for(int i=0;errors.size()>0&&i<5;i++){
  129. int r = random.nextInt(errors.size());
  130. total.add(errors.get(r));
  131. left--;
  132. errors.remove(errors.get(r));
  133. }
  134. //随机选择5道和错题知识点相同的题目
  135. List<String> errKnows = new ArrayList<>();
  136. for(int i=0;i<total.size();i++){
  137. List<String> knows = questionService.getKnowledgesById(total.get(i));
  138. //排除基础的SR类
  139. for(String know : knows){
  140. if(know.split("\\.").length == 1){
  141. knows.remove(know);
  142. break;
  143. }
  144. }
  145. //逐个搜索
  146. for(String know : knows){
  147. List<String> search = new ArrayList<>();
  148. search.add(know);
  149. List<QuestionVO> questions = questionService.searchQuestions(search);
  150. for(QuestionVO questionVO : questions){
  151. String questionId = questionVO.getId();
  152. if(total.contains(questionId)==false && errKnows.contains(questionId)==false){
  153. errKnows.add(questionId);
  154. }
  155. }
  156. }
  157. }
  158. //得到相似错题后,选择5道
  159. for(int i=0;errKnows.size()>0&&i<5;i++){
  160. int r = random.nextInt(errKnows.size());
  161. total.add(errKnows.get(r));
  162. left--;
  163. errKnows.remove(errKnows.get(r));
  164. }
  165. //随机选择15道和低分类似类似的题目
  166. List<String> similar = new ArrayList<>();
  167. int index = 0;
  168. for(Map.Entry<String,Double> entry : afterSort.entrySet()){
  169. if(index>=5){
  170. break;
  171. }
  172. String tar = entry.getKey();
  173. if(tar.split("\\.").length>1){
  174. List<String> ks = new ArrayList<>();
  175. ks.add(tar);
  176. List<QuestionVO> questions = questionService.searchQuestions(ks);
  177. for(QuestionVO questionVO : questions){
  178. String questionId = questionVO.getId();
  179. if(total.contains(questionId)==false && similar.contains(questionId)==false){
  180. similar.add(questionId);
  181. }
  182. }
  183. }else {
  184. break;
  185. }
  186. index++;
  187. }
  188. //得到所有相似题目的题号后,选择15道
  189. for(int i=0;similar.size()>0&&i<15;i++){
  190. int r = random.nextInt(similar.size());
  191. total.add(similar.get(r));
  192. left--;
  193. similar.remove(similar.get(r));
  194. }
  195. //从大类中随机选择剩下的所有题目
  196. List<QuestionVO> questions = questionService.searchQuestions(tarKnows);
  197. List<String> undos = new ArrayList<>();
  198. for(QuestionVO questionVO : questions){
  199. String questionId = questionVO.getId();
  200. if(total.contains(questionId)==false){
  201. undos.add(questionId);
  202. }
  203. }
  204. while(undos.size()>0 && left > 0){
  205. int r = random.nextInt(undos.size());
  206. total.add(undos.get(r));
  207. left--;
  208. undos.remove(undos.get(r));
  209. }
  210. // for(String s : total){
  211. // System.out.println(s);
  212. // }
  213. // System.out.println(total.size());
  214. return total;
  215. }
  216. }