'A', 2 => 'B', 3 => 'C', 4 => 'D', 5 => 'E', 6 => 'F', ]; protected $judge = [ 1 => '对', 2 => '错', ]; protected $type_arr = [ 1 => '单选', 2 => '多选', 3 => '判断' ]; protected static $instance; protected static $error_message = null; public function __construct() { $this->exam = model(TrainExam::class); $this->examResult = model(TrainExamResult::class); $this->question = model(QuestionModel::class); } /** * 初始化 * @param array $options * @return object|static * @author matielong */ public static function instance($options = []) { if (is_null(self::$instance)) { self::$instance = new static($options); } return self::$instance; } public function getError() { return self::$error_message; } protected function setError($msg) { self::$error_message = $msg; return false; } /** * 获取未完成的考试 * @param $phone * @return bool|\think\Collection */ public function getUndoneExam($phone) { try { $admin = $this->getAdminByPhone($phone); if(!$admin){ return false; } $admin_id = $admin['id']; $data = $this->examResult->alias('result') ->join($this->exam->getTable().' exam', 'exam.id = exam_id') ->where('admin_id', $admin_id) ->where(function ($query){ $query->where('result.status', 0) ->whereOr('result.status', 1); }) ->field('result.id as result_id, exam_id, title, duration, exam.start_time, end_time, remark, result.status as result_status, result.start_time as result_start_time') ->select(); foreach ($data as &$val){ $val['residue_time'] = ''; if($val['result_status'] === 1){ $val['residue_time'] = date('H:i:s', ($val['duration'] * 60) - time()); } } unset($val); return $data; } catch (DbException $exception){ return false; } } /** * 获取及格/不集合的考试 * @param $phone * @param false $unqualified * @return array|false */ public function getQualifiedExam($phone, $unqualified = false) { try { $admin = $this->getAdminByPhone($phone); if(!$admin){ return false; } $admin_id = $admin['id']; $where = [ 'admin_id' => $admin_id, 'result.status' => 2, ]; $data = $this->examResult->alias('result') ->join($this->exam->getTable().' exam', 'exam.id = exam_id') ->where($where) ->field('result.id as result_id, exam_id, title, scores, result.start_time, submit_time, total, is_qualified, 0 as newest') ->order('result.id','desc') ->select(); $is_qualified = $unqualified === true ? 0 :1; $result = []; if($data){ $data[0]['newest'] = 1; foreach ($data as $val){ if($val['is_qualified'] === $is_qualified){ $temp = $val; // 时长 $temp['duration'] = ''; if($temp['start_time'] && $temp['submit_time']){ $temp['duration'] = date('H:i:s', strtotime($temp['submit_time']) - strtotime($temp['start_time'])); } $result[] = $temp; } } } return $result; } catch (DbException $exception){ return false; } } /** * 获取缺考的考试 * @param $phone * @return bool|\think\Collection */ public function getMissedExam($phone) { try { $admin = $this->getAdminByPhone($phone); if(!$admin){ return false; } $admin_id = $admin['id']; $now = date('Y-m-d H:i:s', Request::instance()->time()); return $this->examResult->alias('result') ->join($this->exam->getTable().' exam', 'exam.id = exam_id') ->where('admin_id', $admin_id) ->where(function ($query){ $query->where('result.status', 0) ->whereOr('result.status', 1); }) ->where('exam.end_time','<', $now) ->field('exam_id, title, scores, exam.start_time, exam.end_time, total') ->order('result.id','desc') ->select(); } catch (DbException $exception){ return false; } } /** * 获取考试的考题 * @param $exam_id * @return array */ public function getExamQuestions($exam_id) { $question_ids = model(TrainExamReQuestion::class) ->where('exam_id', $exam_id) ->column('question_id'); $data = $this->question ->whereIn('id', $question_ids) ->field('class_id, result, is_del, created_at', true) ->order('type') ->select(); $result = []; foreach ($data as $val){ $temp = [ "id" => $val['id'], "title" => $val['title'], "type" => $val['type'], "answer" => [] ]; if($val['answer1']){ $temp['answer'][] = ['key' => 1, 'title' => $val['answer1'], 'checked' => 0]; } if($val['answer2']){ $temp['answer'][] = ['key' => 2, 'title' => $val['answer2'], 'checked' => 0]; } if($val['answer3']){ $temp['answer'][] = ['key' => 3, 'title' => $val['answer3'], 'checked' => 0]; } if($val['answer4']){ $temp['answer'][] = ['key' => 4, 'title' => $val['answer4'], 'checked' => 0]; } if($val['answer5']){ $temp['answer'][] = ['key' => 5, 'title' => $val['answer5'], 'checked' => 0]; } if($val['answer6']){ $temp['answer'][] = ['key' => 6, 'title' => $val['answer6'], 'checked' => 0]; } $result[] = $temp; } return $result; } public function getExam($exam_id) { return $this->exam->get(['id' => $exam_id]); } public function getResult($result_id) { return $this->examResult->get($result_id); } /** * 获取最新未开始的考试 * @param $phone * @param $exam_id * @return array|bool */ public function getNestExamResult($phone, $exam_id) { $admin = $this->getAdminByPhone($phone); if(!$admin){ return false; } $admin_id = $admin['id']; return model(TrainExamResult::class) ->where('admin_id', $admin_id) ->where('exam_id', $exam_id) ->where('status', 0) ->order('id','desc') ->find(); } /** * 开始考试 * @param $result_id * @return bool */ public function startExam($result_id) { $res = model(TrainExamResult::class) ->where('id', $result_id) ->update([ 'status' => 1, 'start_time' => date('Y-m-d H:i:s') ]); if(!$res){ return false; } return true; } /** * 重考 * @param $result_id * @return array|false */ public function retakeExam($result_id) { $result = $this->getResult($result_id); $number = model(TrainExamResult::class) ->where('exam_id', $result['exam_id']) ->where('admin_id', $result['admin_id']) ->max('number') + 1; $res = model(TrainExamResult::class) ->insertGetId([ 'exam_id' => $result['exam_id'], 'admin_id' => $result['admin_id'], 'number' => $number, 'status' => 1, 'start_time' => date('Y-m-d H:i:s') ]); return $res !== false ? ['result_id' => $res] : false; } /** * 获取考题column * @param $question_ids * @return array|false|string */ public function getQuestionColumn($question_ids) { return $this->question ->whereIn('id', $question_ids) ->column('id, type, result'); } /** * 提交考试 * @param $result_id * @param $record * @return array */ public function subExam($result_id, $record) { // 创建答题记录 Db::startTrans(); $record_save_res = $this->insertResultRecord($result_id, $record); if(!$record_save_res){ Db::rollback(); $this->setError('插入答题记录失败'); } // 更新考卷数据 $result = $this->getResult($result_id); $total = $this->calculateScore($result['exam_id'], $result_id); $exam = $this->getExam($result['exam_id']); $is_qualified = $total >= $exam['qualified'] ? 1 : 0; $update_res = $this->examResult ->where('id', $result_id) ->update([ 'scores' => $total, 'status' => 2, 'is_qualified' => $is_qualified, 'submit_time' => date('Y-m-d H:i:s', Request::instance()->time()) ]); if(!$update_res){ Db::rollback(); $this->setError('更新考卷数据失败 '); } // ok Db::commit(); return [ 'scores' => $total, 'qualified' => $is_qualified, ]; } /** * 插入答题记录 * @param $result_id * @param $records * @return bool */ public function insertResultRecord($result_id, $records) { $question_ids = array_column($records, 'question_id'); $question_arr = $this->getQuestionColumn($question_ids); $save = []; foreach ($records as $record){ if(!$record){ continue; } $question_id = $record['question_id']; if(!isset($question_arr[$question_id])){ continue; } // 格式化答案 $right_res = $question_arr[$question_id]['result']; $checked_arr = []; foreach ($record['answer'] as $answer){ if((int) $answer['checked'] === 1){ $checked_arr[] = (int) $answer['key']; } } // 判断对错 $question_answer = implode('', $checked_arr); $result = $this->checkQuestionResult($question_answer, $right_res) ? 1 : 0; // 组装数据 $save[] = [ 'result_id' => $result_id, 'question_id' => $question_id, 'answer' => $question_answer, 'result' => $result, ]; } $res = model(TrainExamResultRecord::class) ->insertAll($save); return $res !== false; } /** * 检查题目是否正确 * @param $answer * @param $right_result * @return bool */ public function checkQuestionResult($answer, $right_result) { $answer_arr = $this->ch2arr((int) $answer); $right_arr = $this->ch2arr((int) $right_result); if(array_diff($answer_arr, $right_arr)){ return false; } return true; } /** * 计算得分 * @param $exam_id * @param $result_id * @return float|int|string */ public function calculateScore($exam_id, $result_id) { $right_question_ids = model(TrainExamResultRecord::class) ->where('result_id', $result_id) ->where('result', 1) ->column('question_id'); return model(TrainExamReQuestion::class) ->where('exam_id', $exam_id) ->whereIn('question_id', $right_question_ids) ->sum('score'); } /** * 字符串分割数组 * @param $str * @return array */ public function ch2arr($str) { $length = mb_strlen($str, 'utf-8'); $array = []; for ($i = 0; $i < $length; $i++) $array[] = mb_substr($str, $i, 1, 'utf-8'); return $array; } /** * 获取考试结果根据用户id * @param $exam_id * @param $admin_ids * @return array */ public function getResultByAdminIds($exam_id, $admin_ids) { $data = model(TrainExamResult::class) ->where('exam_id', $exam_id) ->whereIn('admin_id', $admin_ids) ->field('id, admin_id, number, status, is_qualified, scores') ->order('id','desc') ->select(); $result = []; foreach ($data as $val){ if(!isset($result[$val['admin_id']])){ $result[$val['admin_id']] = [ 'result_id' => $val['id'], 'status' => $val['status'], 'scores' => $val['scores'], 'is_qualified' => $val['is_qualified'] ]; } } return $result; } /** * 获取考试剩余时间 * @param $result_id * @return false|float|int */ public function getRemainingTime($result_id) { $result = $this->getResult($result_id); $exam = $this->getExam($result['exam_id']); return strtotime($result['start_time']) + ($exam['duration'] * 60) - Request::instance()->time(); } public function formatResult($result, $type) { switch ($type){ case 1: case 2: $str = (string) $result; $iMax = strlen($str); $temp = []; for($i=0; $i< $iMax; $i++){//遍历字符串追加给数组 $temp[] = $this->result_arr[$str[$i]]; } return implode($temp); case 3: return $this->judge[$result] ?? $result; default; } } public function formatType($type){ return $this->type_arr[$type] ?? $type; } public function getAdminByPhone($phone) { return model(TrainAdmin::class) ->where('mobile', $phone) ->find(); } }