QuestionModel.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. <?php
  2. namespace app\admin\model\exam;
  3. use addons\exam\model\BaseModel;
  4. use think\Collection;
  5. use think\Db;
  6. use think\db\exception\DataNotFoundException;
  7. use think\db\exception\ModelNotFoundException;
  8. use think\exception\DbException;
  9. use think\Model;
  10. use traits\model\SoftDelete;
  11. class QuestionModel extends BaseModel
  12. {
  13. use SoftDelete;
  14. // 表名
  15. protected $name = 'exam_question';
  16. // 自动写入时间戳字段
  17. protected $autoWriteTimestamp = 'int';
  18. // 定义时间戳字段名
  19. protected $createTime = 'createtime';
  20. protected $updateTime = 'updatetime';
  21. protected $deleteTime = 'deletetime';
  22. // 追加属性
  23. protected $append
  24. = [
  25. 'kind_text',
  26. 'difficulty_text',
  27. 'status_text',
  28. 'title_video_url',
  29. 'explain_video_url',
  30. ];
  31. const kindList = ['JUDGE', 'SINGLE', 'MULTI', 'FILL', 'SHORT', 'MATERIAL'];
  32. const difficultyList = ['EASY', 'GENERAL', 'HARD'];
  33. const statusList = ['NORMAL', 'HIDDEN'];
  34. public function getKindList()
  35. {
  36. return [
  37. 'JUDGE' => '判断题',
  38. 'SINGLE' => '单选题',
  39. 'MULTI' => '多选题',
  40. 'FILL' => '填空题',
  41. 'SHORT' => '简答题',
  42. 'MATERIAL' => '材料题',
  43. ];
  44. }
  45. public function getDifficultyList()
  46. {
  47. return ['EASY' => '简单', 'GENERAL' => '普通', 'HARD' => '困难'];
  48. }
  49. public function getStatusList()
  50. {
  51. return ['NORMAL' => '正常', 'HIDDEN' => '隐藏'];
  52. }
  53. public function getKindTextAttr($value, $data)
  54. {
  55. $value = $value ? $value : ($data['kind'] ?? '');
  56. $list = $this->getKindList();
  57. return $list[$value] ?? '';
  58. }
  59. public function getDifficultyTextAttr($value, $data)
  60. {
  61. $value = $value ? $value : ($data['difficulty'] ?? '');
  62. $list = $this->getDifficultyList();
  63. return $list[$value] ?? '';
  64. }
  65. public function getStatusTextAttr($value, $data)
  66. {
  67. $value = $value ? $value : ($data['status'] ?? '');
  68. $list = $this->getStatusList();
  69. return $list[$value] ?? '';
  70. }
  71. public function getTitleVideoUrlAttr($value, $data)
  72. {
  73. $value = $data['title_video'] ?? '';
  74. return cdnurl($value, true);
  75. }
  76. public function getExplainVideoUrlAttr($value, $data)
  77. {
  78. $value = $data['explain_video'] ?? '';
  79. return cdnurl($value, true);
  80. }
  81. public function cate()
  82. {
  83. return $this->belongsTo(CateModel::class, 'cate_id', 'id', [], 'LEFT')->setEagerlyType(0);
  84. }
  85. public function cates()
  86. {
  87. return $this->belongsTo(CateModel::class, 'cate_id');
  88. }
  89. /**
  90. * 材料题子题目
  91. *
  92. * @return \think\model\relation\HasMany
  93. */
  94. public function materialQuestions()
  95. {
  96. return $this->hasMany(MaterialQuestionModel::class, 'parent_question_id', 'id')->order('weigh');
  97. }
  98. public function materialParent()
  99. {
  100. return $this->belongsTo(self::class, 'material_question_id', 'id');
  101. }
  102. protected function scopeCate($query, $cate_ids)
  103. {
  104. $query->whereIn('cate_id', $cate_ids);
  105. }
  106. protected function scopeKind($query, $kind, $limit = 0)
  107. {
  108. $query->where('kind', $kind);
  109. if ($limit) {
  110. $query->limit($limit);
  111. }
  112. }
  113. protected function scopeDifficulty($query, $difficulty, $limit = 0)
  114. {
  115. $query->where('difficulty', $difficulty);
  116. }
  117. /**
  118. * 获取多题库下各类题型的不同难度题数
  119. *
  120. * @param $cate_ids
  121. * @return array|bool|\PDOStatement|string|Model|null
  122. */
  123. public function getCount($cate_ids)
  124. {
  125. return Db::name($this->name)
  126. ->whereIn('cate_id', $cate_ids)
  127. ->where('is_material_child', 0) // 材料题子题不计入总数
  128. ->where('status', 'NORMAL')
  129. ->whereNull('deletetime')
  130. ->field("
  131. COUNT(id) as 'total',
  132. COUNT(CASE WHEN kind = 'JUDGE' then 1 END) as 'judge',
  133. COUNT(CASE WHEN kind = 'JUDGE' and difficulty = 'EASY' then 1 END) as 'judge_easy',
  134. COUNT(CASE WHEN kind = 'JUDGE' and difficulty = 'GENERAL' then 1 END) as 'judge_general',
  135. COUNT(CASE WHEN kind = 'JUDGE' and difficulty = 'HARD' then 1 END) as 'judge_hard',
  136. COUNT(CASE WHEN kind = 'SINGLE' then 1 END) as 'single',
  137. COUNT(CASE WHEN kind = 'SINGLE' and difficulty = 'EASY' then 1 END) as 'single_easy',
  138. COUNT(CASE WHEN kind = 'SINGLE' and difficulty = 'GENERAL' then 1 END) as 'single_general',
  139. COUNT(CASE WHEN kind = 'SINGLE' and difficulty = 'HARD' then 1 END) as 'single_hard',
  140. COUNT(CASE WHEN kind = 'MULTI' then 1 END) as 'multi',
  141. COUNT(CASE WHEN kind = 'MULTI' and difficulty = 'EASY' then 1 END) as 'multi_easy',
  142. COUNT(CASE WHEN kind = 'MULTI' and difficulty = 'GENERAL' then 1 END) as 'multi_general',
  143. COUNT(CASE WHEN kind = 'MULTI' and difficulty = 'HARD' then 1 END) as 'multi_hard',
  144. COUNT(CASE WHEN kind = 'FILL' then 1 END) as 'fill',
  145. COUNT(CASE WHEN kind = 'FILL' and difficulty = 'EASY' then 1 END) as 'fill_easy',
  146. COUNT(CASE WHEN kind = 'FILL' and difficulty = 'GENERAL' then 1 END) as 'fill_general',
  147. COUNT(CASE WHEN kind = 'FILL' and difficulty = 'HARD' then 1 END) as 'fill_hard',
  148. COUNT(CASE WHEN kind = 'SHORT' then 1 END) as 'short',
  149. COUNT(CASE WHEN kind = 'SHORT' and difficulty = 'EASY' then 1 END) as 'short_easy',
  150. COUNT(CASE WHEN kind = 'SHORT' and difficulty = 'GENERAL' then 1 END) as 'short_general',
  151. COUNT(CASE WHEN kind = 'SHORT' and difficulty = 'HARD' then 1 END) as 'short_hard',
  152. COUNT(CASE WHEN kind = 'MATERIAL' then 1 END) as 'material',
  153. COUNT(CASE WHEN kind = 'MATERIAL' and difficulty = 'EASY' then 1 END) as 'material_easy',
  154. COUNT(CASE WHEN kind = 'MATERIAL' and difficulty = 'GENERAL' then 1 END) as 'material_general',
  155. COUNT(CASE WHEN kind = 'MATERIAL' and difficulty = 'HARD' then 1 END) as 'material_hard'
  156. ")->find();
  157. }
  158. /*
  159. * 根据关键词模糊查询10条题目
  160. * Robin
  161. * */
  162. // public function getList($params)
  163. // {
  164. // $param = array_merge([
  165. // 'keyword' => '', // 搜索关键词
  166. // 'sortType' => '', // 排序类型
  167. // 'listRows' => 20, // 每页数量
  168. // 'sortRand' => 0 //是否随机查询,0 or 1
  169. // ], $params);
  170. //
  171. // if ($param['keyword'])
  172. // $this->where('title', 'like', '%' . $param['keyword'] . '%');
  173. //
  174. // if ($param['sortType'])
  175. // $this->order($param['sortType']);
  176. //
  177. // if ($param['sortRand'])
  178. // $this->orderRaw('rand()');
  179. //
  180. // return $this->with('collected')->where('cate_id', intval($param['cate_id']))
  181. // ->where('status', 'NORMAL')
  182. // ->paginate($param['listRows'], true);
  183. // }
  184. /**
  185. * 获取题目列表
  186. *
  187. * @param string $cates 分类ID,多个逗号隔开
  188. * @param string $kind 题型
  189. * @param array $with 关联模型
  190. * @return QuestionModel
  191. */
  192. public static function getListByCateAndKind($cates, $kind, $with = [])
  193. {
  194. return self::with($with)
  195. ->whereIn('cate_id', $cates)
  196. ->where('kind', $kind)
  197. ->where('is_material_child', 0)// 材料题子题不显示
  198. ->orderRaw('rand()');
  199. }
  200. /**
  201. * 获取试卷固定题目
  202. *
  203. * @param int $paper_id 试卷ID
  204. * @param array $with 关联模型
  205. * @return bool|\PDOStatement|string|Collection
  206. * @throws DataNotFoundException
  207. * @throws ModelNotFoundException
  208. * @throws DbException
  209. */
  210. public static function getFixListByPaper($paper_id, $with = [])
  211. {
  212. $questions = self::with($with)
  213. ->alias('question_model')
  214. ->join('exam_paper_question pq', 'question_model.id = pq.question_id')
  215. ->where('pq.paper_id', $paper_id)
  216. ->where('is_material_child', 0)
  217. ->field('question_model.*, pq.score, pq.sort, pq.answer_config')
  218. ->order('pq.sort', 'desc')
  219. ->select();
  220. foreach ($questions as &$question) {
  221. if (!empty($question['answer_config'])) {
  222. // 简答题 - 替换答案分数配置
  223. if ($question['kind'] == 'SHORT') {
  224. $question['answer'] = $question['answer_config'];
  225. }
  226. }
  227. }
  228. return $questions;
  229. }
  230. /**
  231. * 记录错题
  232. */
  233. public function logWrong($user_id, $user_answer = null)
  234. {
  235. // if ($item = QuestionWrongModel::where('user_id', $user_id)
  236. // ->where('question_id', $this->id)
  237. // ->find()) {
  238. // $item->user_answer = $user_answer;
  239. // $item->save();
  240. //
  241. // return $item;
  242. // } else {
  243. // return QuestionWrongModel::create([
  244. // 'user_id' => $user_id,
  245. // 'question_id' => $this->id,
  246. // 'user_answer' => $user_answer,
  247. // ]);
  248. // }
  249. if (is_array($user_answer)) {
  250. $user_answer = json_encode($user_answer, JSON_UNESCAPED_UNICODE);
  251. } else if (is_string($user_answer)) {
  252. $user_answer = trim($user_answer);
  253. if (strpos($user_answer, ',')) {
  254. $user_answer = json_encode(explode(',', $user_answer), JSON_UNESCAPED_UNICODE);
  255. }
  256. } else {
  257. $user_answer = null;
  258. }
  259. return QuestionWrongModel::updateOrCreate(
  260. [
  261. 'user_id' => $user_id,
  262. 'question_id' => $this->id,
  263. ],
  264. [
  265. 'user_id' => $user_id,
  266. 'question_id' => $this->id,
  267. 'user_answer' => $user_answer, //is_array($user_answer) ? json_encode($user_answer, JSON_UNESCAPED_UNICODE) : $user_answer,
  268. ]
  269. );
  270. }
  271. /**
  272. * 记录错题
  273. *
  274. * @param string $question_kind 题型
  275. * @param int $question_id 题目ID
  276. * @param int $user_id 用户ID
  277. * @param null $user_answer 用户答案
  278. * @param string $source 来源:PAPER=试卷,ROOM=考场,TRAINING=练题
  279. * @return mixed
  280. */
  281. public static function recordWrong($question_kind, $question_id, $user_id, $user_answer = null, $source = 'PAPER', $source_data = [])
  282. {
  283. if (is_array($user_answer)) {
  284. $user_answer = json_encode($user_answer, JSON_UNESCAPED_UNICODE);
  285. } else if (is_string($user_answer)) {
  286. $user_answer = trim($user_answer);
  287. if (in_array($question_kind, ['JUDGE', 'SINGLE', 'MULTI'])) {
  288. $user_answer = strtoupper($user_answer);
  289. } else if (strpos($user_answer, ',')) {
  290. $user_answer = json_encode(explode(',', $user_answer), JSON_UNESCAPED_UNICODE);
  291. }
  292. } else {
  293. $user_answer = null;
  294. }
  295. // 按每次错题记录
  296. return QuestionWrongModel::create(
  297. [
  298. 'user_id' => $user_id,
  299. 'question_id' => $question_id,
  300. 'user_answer' => $user_answer,
  301. 'kind' => $source,
  302. 'cate_id' => $source_data['cate_id'] ?? 0,
  303. 'paper_id' => $source_data['paper_id'] ?? 0,
  304. 'room_id' => $source_data['room_id'] ?? 0,
  305. ]
  306. );
  307. // 相同题目仅记录最后一次错题
  308. // return QuestionWrongModel::updateOrCreate(
  309. // [
  310. // 'user_id' => $user_id,
  311. // 'question_id' => $question_id,
  312. // 'kind' => $source,
  313. // 'cate_id' => $source_data['cate_id'] ?? 0,
  314. // 'paper_id' => $source_data['paper_id'] ?? 0,
  315. // 'room_id' => $source_data['room_id'] ?? 0,
  316. // ],
  317. // [
  318. // 'user_id' => $user_id,
  319. // 'question_id' => $question_id,
  320. // 'user_answer' => $user_answer, //is_array($user_answer) ? json_encode($user_answer, JSON_UNESCAPED_UNICODE) : $user_answer,
  321. // 'kind' => $source,
  322. // 'cate_id' => $source_data['cate_id'] ?? 0,
  323. // 'paper_id' => $source_data['paper_id'] ?? 0,
  324. // 'room_id' => $source_data['room_id'] ?? 0,
  325. // ]
  326. // );
  327. }
  328. }