|
|
@@ -13,11 +13,13 @@ namespace App\Services\Api;
|
|
|
|
|
|
use App\Models\ExamAccessLogModel;
|
|
|
use App\Models\ExamAnswerModel;
|
|
|
+use App\Models\ExamAnswerTopicModel;
|
|
|
use App\Models\ExamPaperModel;
|
|
|
use App\Models\ExamTopicModel;
|
|
|
use App\Models\MemberAnswerRankModel;
|
|
|
use App\Services\BaseService;
|
|
|
use App\Services\ConfigService;
|
|
|
+use App\Services\DeepSeekService;
|
|
|
use App\Services\RedisService;
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
|
|
@@ -68,7 +70,7 @@ class ExamService extends BaseService
|
|
|
}
|
|
|
|
|
|
$query = $this->getQuery($params);
|
|
|
- $list = $query->select(['a.id', 'a.user_id', 'a.paper_id', 'a.score', 'a.accurate_count', 'b.name', 'b.type', 'b.topic_count', 'b.score_total', 'b.is_charge', 'a.create_time', 'a.answer_times', 'a.status'])
|
|
|
+ $list = $query->select(['a.id', 'a.user_ids', 'a.paper_id', 'a.score', 'a.accurate_count', 'b.name', 'b.type', 'b.topic_count', 'b.score_total', 'b.is_charge', 'a.create_time', 'a.answer_times', 'a.status'])
|
|
|
->orderBy('a.create_time', 'desc')
|
|
|
->paginate($pageSize > 0 ? $pageSize : 9999999);
|
|
|
$list = $list ? $list->toArray() : [];
|
|
|
@@ -85,7 +87,7 @@ class ExamService extends BaseService
|
|
|
'list' => $rows
|
|
|
];
|
|
|
if ($rows) {
|
|
|
- RedisService::set($cacheKey, $datas, rand(300, 600));
|
|
|
+ RedisService::set($cacheKey, $datas, rand(30, 60));
|
|
|
}
|
|
|
|
|
|
return $datas;
|
|
|
@@ -142,14 +144,15 @@ class ExamService extends BaseService
|
|
|
public function getHistoryList($params, $pageSize = 10)
|
|
|
{
|
|
|
$page = isset($params['page']) ? $params['page'] : 1;
|
|
|
- $cacheKey = "caches:exams:history_{$page}_{$pageSize}:" . md5(json_encode($params));
|
|
|
+ $userId = isset($params['user_id']) ? $params['user_id'] : 0;
|
|
|
+ $cacheKey = "caches:exams:{$userId}_history_{$page}:" . md5(json_encode($params));
|
|
|
$datas = RedisService::get($cacheKey);
|
|
|
if ($datas) {
|
|
|
return $datas;
|
|
|
}
|
|
|
|
|
|
$query = $this->getQuery($params);
|
|
|
- $list = $query->select(['a.id', 'a.user_id', 'a.paper_id', 'a.score', 'a.accurate_count', 'b.name', 'b.type', 'b.topic_count', 'b.score_total', 'b.is_charge', 'a.create_time', 'a.answer_times', 'a.status'])
|
|
|
+ $list = $query->select(['a.id', 'a.user_id', 'a.paper_id', 'a.score', 'a.accurate_count', 'b.name', 'b.type','b.scene_type', 'b.topic_count', 'b.score_total', 'b.is_charge', 'a.create_time', 'a.answer_times', 'a.status'])
|
|
|
->orderBy('a.create_time', 'desc')
|
|
|
->paginate($pageSize > 0 ? $pageSize : 9999999);
|
|
|
$list = $list ? $list->toArray() : [];
|
|
|
@@ -183,7 +186,7 @@ class ExamService extends BaseService
|
|
|
{
|
|
|
$page = isset($params['page']) ? $params['page'] : 1;
|
|
|
$type = isset($params['type']) ? $params['type'] : 1;
|
|
|
- $cacheKey = "caches:exams:practice_{$userId}:{$page}_" . md5(json_encode($params));
|
|
|
+ $cacheKey = "caches:exams:{$userId}_practice:{$page}_" . md5(json_encode($params));
|
|
|
$datas = RedisService::get($cacheKey);
|
|
|
// 每日一练访问次数统计
|
|
|
if(empty($sc)){
|
|
|
@@ -196,7 +199,7 @@ class ExamService extends BaseService
|
|
|
|
|
|
$list = $this->model->from('exam_answers as a')
|
|
|
->leftJoin('exam_papers as b', 'b.id', '=', 'a.paper_id')
|
|
|
- ->where(['b.scene_type' => 1, 'b.status' => 1, 'b.mark' => 1, 'a.mark' => 1])
|
|
|
+ ->where(['b.scene_type' => 1, 'b.status' => 1,'a.status'=>1, 'b.mark' => 1, 'a.mark' => 1])
|
|
|
->where(function ($query) use ($params) {
|
|
|
$type = isset($params['type']) && $params['type'] ? intval($params['type']) : 1;
|
|
|
if ($type > 0) {
|
|
|
@@ -284,12 +287,30 @@ class ExamService extends BaseService
|
|
|
return $datas;
|
|
|
}
|
|
|
|
|
|
+ public function getCardList($userId, $paperId, $rid=0)
|
|
|
+ {
|
|
|
+ $cacheKey = "caches:exams:{$userId}_cardList:{$paperId}_{$rid}";
|
|
|
+ $datas = RedisService::get($cacheKey);
|
|
|
+ if($datas){
|
|
|
+ return $datas;
|
|
|
+ }
|
|
|
+
|
|
|
+ $datas = ExamTopicModel::from('exam_topics as a')
|
|
|
+ ->leftJoin('exam_answers_topics as b',function($join) use($rid){
|
|
|
+ if($rid){
|
|
|
+ $join->
|
|
|
+ }
|
|
|
+ })
|
|
|
+ ->where(['a.paper_id'=> $paperId, 'a.status'=>1,'a.mark'=>1])
|
|
|
+ ->select(['a.*','b.id'])
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 重新答题,清除答题记录数据
|
|
|
* @param $id
|
|
|
* @return bool
|
|
|
*/
|
|
|
- public function reset($id)
|
|
|
+ public function reset($id, $userId=0)
|
|
|
{
|
|
|
$log = $this->model->where(['id'=> $id,'mark'=>1])->first();
|
|
|
if(empty($log)){
|
|
|
@@ -298,19 +319,27 @@ class ExamService extends BaseService
|
|
|
|
|
|
$updateData = ['score'=>0,'accurate_count'=>0,'answer_count'=>0,'answer_times'=>0,'answer_last_id'=>0,'is_submit'=>0,'status'=>1];
|
|
|
$this->model->where(['id'=> $id,'mark'=>1])->update($updateData);
|
|
|
+ ExamAnswerTopicModel::where(['answer_log_id'=> $id,'mark'=>1])->update(['status'=>2,'update_time'=>time()]);
|
|
|
+ RedisService::keyDel("caches:exams:{$userId}*");
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 答题
|
|
|
+ * @param $userId
|
|
|
+ * @param $params
|
|
|
+ * @return array|false
|
|
|
+ */
|
|
|
public function answer($userId, $params)
|
|
|
{
|
|
|
$paperId = isset($params['id'])? $params['id'] : 0;
|
|
|
$rid = isset($params['rid'])? $params['rid'] : 0;
|
|
|
$tid = isset($params['tid'])? $params['tid'] : 0;
|
|
|
- $type = isset($params['type'])? $params['type'] : 1;
|
|
|
$isSubmit = isset($params['is_submit'])? $params['is_submit'] : 1;
|
|
|
$answer = isset($params['answer'])? $params['answer'] : '';
|
|
|
$answerImage = isset($params['answer_image'])? $params['answer_image'] : '';
|
|
|
$answerType = isset($params['answer_type']) && $params['answer_type']? $params['answer_type'] : 2;
|
|
|
+ $remainTime = isset($params['remain_time']) && $params['remain_time']? $params['remain_time'] : 0;
|
|
|
if($isSubmit<=0 && empty($answer)){
|
|
|
$this->error = '请先提交答案';
|
|
|
return false;
|
|
|
@@ -321,12 +350,22 @@ class ExamService extends BaseService
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ $cacheKey = "caches:answers:{$userId}_{$paperId}:{$tid}_{$rid}";
|
|
|
+ if(RedisService::get($cacheKey.'_lock')){
|
|
|
+ $this->error = '请不要频繁提交';
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ RedisService::set($cacheKey.'_lock', $params, rand(2,3));
|
|
|
+
|
|
|
// 试卷数据
|
|
|
- $paperInfo = ExamPaperModel::where(['id'=> $paperId,'type'=>$type,'status'=>1,'mark'=>1])
|
|
|
+ $paperInfo = ExamPaperModel::where(['id'=> $paperId,'status'=>1,'mark'=>1])
|
|
|
->first();
|
|
|
- $correctAnswer = isset($paperInfo['correct_answer'])? $paperInfo['correct_answer'] : '';
|
|
|
+ $type = isset($paperInfo['type']) && $paperInfo['type']? $paperInfo['type'] : 1;
|
|
|
$sceneType = isset($paperInfo['scene_type']) && $paperInfo['scene_type']? $paperInfo['scene_type'] : 1;
|
|
|
+ $topicCount = isset($paperInfo['topic_count'])? $paperInfo['topic_count'] : 0;
|
|
|
if(empty($paperInfo)){
|
|
|
+ RedisService::clear($cacheKey.'_lock');
|
|
|
$this->error = '试题数据错误,请返回刷新重试';
|
|
|
return false;
|
|
|
}
|
|
|
@@ -334,19 +373,31 @@ class ExamService extends BaseService
|
|
|
// 题目数据
|
|
|
$topicInfo = ExamTopicModel::where(['id'=> $tid,'paper_id'=>$paperId,'status'=>1,'mark'=>1])->first();
|
|
|
$topicType = isset($topicInfo['topic_type'])? trim($topicInfo['topic_type']) : '';
|
|
|
- if(empty($topicInfo) || empty($topicType)){
|
|
|
+ $topicScore = isset($topicInfo['score'])? intval($topicInfo['score']) : 0;
|
|
|
+ $topicName = isset($topicInfo['topic_name'])? $topicInfo['topic_name'] : '';
|
|
|
+ $topicShowType = isset($topicInfo['show_type'])? $topicInfo['show_type'] : 1;
|
|
|
+ $correctAnswer = isset($topicInfo['correct_answer'])? $topicInfo['correct_answer'] : '';
|
|
|
+ if(empty($topicInfo) || empty($topicType) || empty($topicName)){
|
|
|
+ RedisService::clear($cacheKey.'_lock');
|
|
|
$this->error = '题库已更新,请返回刷新重试';
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 答题记录
|
|
|
$submit = 0;
|
|
|
+ $answerCount = 0;
|
|
|
if($rid){
|
|
|
$answerInfo = ExamAnswerModel::where(['id'=>$rid,'status'=>1,'mark'=>1])->first();
|
|
|
$submit = isset($answerInfo['is_submit'])? $answerInfo['is_submit'] : 0;
|
|
|
+ $answerCount = isset($answerInfo['answer_count'])? $answerInfo['answer_count'] : 0;
|
|
|
if(empty($answerInfo)){
|
|
|
$rid = 0;
|
|
|
}
|
|
|
+
|
|
|
+ if($submit){
|
|
|
+ $this->error = '您已交卷';
|
|
|
+ return false;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 验证答案内容类型和数据
|
|
|
@@ -356,38 +407,216 @@ class ExamService extends BaseService
|
|
|
$answerInfo = ExamAnswerModel::where(['paper_id'=>$paperId,'status'=>1,'mark'=>1])->where('create_time','>=', strtotime(date('Y-m-d')))->first();
|
|
|
$rid = isset($answerInfo['id'])? $answerInfo['id'] : 0;
|
|
|
$submit = isset($answerInfo['is_submit'])? $answerInfo['is_submit'] : 0;
|
|
|
+ $answerCount = isset($answerInfo['answer_count'])? $answerInfo['answer_count'] : 0;
|
|
|
}
|
|
|
|
|
|
// 是否已交卷
|
|
|
if($submit == 1){
|
|
|
+ RedisService::clear($cacheKey.'_lock');
|
|
|
$this->error = '您已交卷';
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- /* TODO 验证答案 */
|
|
|
- if(in_array($topicType,['选择题','单选题'])){
|
|
|
+ // 直接交卷
|
|
|
+ if($isSubmit==1 && empty($answer)){
|
|
|
+ // 是否提交过
|
|
|
+ if($answerCount<=0){
|
|
|
+ $this->error = '您未提交过答案,请先答题再交卷';
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ $this->model->where(['id'=> $rid])->update(['is_submit'=>1,'update_time'=>time()]);
|
|
|
+ $this->error = '交卷成功';
|
|
|
+ RedisService::keyDel("caches:exams:{$userId}*");
|
|
|
+ return ['rid'=> $rid,'paper_id'=>$paperId];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 该题是否已提交答案
|
|
|
+ $logId = 0;
|
|
|
+ if($rid>0){
|
|
|
+ $answerTopic = ExamAnswerTopicModel::where(['answer_log_id'=>$rid,'topic_id'=> $tid,'mark'=>1])->first();
|
|
|
+ $accurate = isset($answerTopic['accurate'])? $answerTopic['accurate'] : -1;
|
|
|
+ $logId = isset($answerTopic['id'])? $answerTopic['id'] : 0;
|
|
|
+ $status = isset($answerTopic['status'])? $answerTopic['status'] : 0;
|
|
|
+ if($answerTopic && $accurate>=0 && $status == 1){
|
|
|
+ RedisService::clear($cacheKey.'_lock');
|
|
|
+ $this->error = '该题答案已提交';
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- } else if (in_array($topicType, ['简答题','计算题','阅读理解'])){
|
|
|
+ /* TODO 验证答案 */
|
|
|
+ $submitType = 1;
|
|
|
+ $topicData = [
|
|
|
+ 'user_id'=> $userId,
|
|
|
+ 'topic_id'=> $tid,
|
|
|
+ 'answer'=> $answerType==1? get_image_path($answer) : $answer,
|
|
|
+ 'score'=>0,
|
|
|
+ 'accurate'=>0,
|
|
|
+ 'create_time'=> time(),
|
|
|
+ 'status'=> 1,
|
|
|
+ 'mark'=> 1,
|
|
|
+ ];
|
|
|
+ if(in_array($topicType,['选择题','单选题','多选题','填空题'])){
|
|
|
+ $topicData['answer_analysis'] = isset($topicInfo['answer_analysis'])? $topicInfo['answer_analysis'] : '';
|
|
|
+ if($answer == $correctAnswer){
|
|
|
+ $topicData['accurate'] = 1;
|
|
|
+ $topicData['score'] = $topicScore;
|
|
|
+ }else{
|
|
|
+ $topicData['accurate'] = 0;
|
|
|
+ }
|
|
|
+// }else if (in_array($topicType, ['简答题','计算题','阅读理解'])){
|
|
|
+ }else {
|
|
|
// 图片答案AI验证
|
|
|
if($answerType == 1){
|
|
|
+ $submitType = 3;
|
|
|
+ $apiData = [
|
|
|
+ 'answer'=> $answer,
|
|
|
+ 'score'=> $topicScore,
|
|
|
+ 'topic'=> $topicShowType == 2 && $topicName? DeepSeekService::make()->getImageTopicData($topicName) : $topicName,
|
|
|
+ ];
|
|
|
+
|
|
|
+ $result = DeepSeekService::make()->apiRequest($apiData);
|
|
|
+ RedisService::clear($cacheKey.'_lock');
|
|
|
+ $this->error = '功能开放中~';
|
|
|
+ return false;
|
|
|
+ }else {
|
|
|
+ $submitType = 2;
|
|
|
+ $apiData = [
|
|
|
+ 'answer'=> $answer,
|
|
|
+ 'score'=> $topicScore,
|
|
|
+ 'topic'=> $topicShowType == 2 && $topicName? DeepSeekService::make()->getImageTopicData($topicName) : $topicName,
|
|
|
+ ];
|
|
|
+
|
|
|
+ $result = DeepSeekService::make()->apiRequest($apiData);
|
|
|
+ $score = isset($result['score'])?$result['score'] : 0;
|
|
|
+ $analysis = isset($result['analysis'])?$result['analysis'] : '';
|
|
|
+ if($score>0){
|
|
|
+ $topicData['accurate'] = 1;
|
|
|
+ $topicData['score'] = $score;
|
|
|
+ $topicData['answer_analysis'] = $analysis;
|
|
|
+ }else{
|
|
|
+ $topicData['accurate'] = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
+ $totalTime = ConfigService::make()->getConfigByCode('answer_total_time', 1800);
|
|
|
+ $answerTime = $totalTime >$remainTime? $totalTime-$remainTime : $totalTime;
|
|
|
+ DB::beginTransaction();
|
|
|
+ if($rid){
|
|
|
+ $log = [
|
|
|
+ 'answer_times'=> $answerTime,
|
|
|
+ 'answer_last_at'=> date('Y-m-d H:i:s'),
|
|
|
+ 'answer_count'=> DB::raw("answer_count + 1"),
|
|
|
+ 'answer_last_id'=> $tid,
|
|
|
+ 'is_submit'=> $isSubmit==1?1:0,
|
|
|
+ 'update_time'=> time()
|
|
|
+ ];
|
|
|
|
|
|
- }else {
|
|
|
+ // 对题数
|
|
|
+ if($topicData['accurate']==1){
|
|
|
+ $log['accurate_count'] = DB::raw("accurate_count + 1");
|
|
|
+ $log['score'] = DB::raw("score + {$topicData['score']}");
|
|
|
+ }
|
|
|
|
|
|
+ if(!$this->model->where(['id'=> $rid])->update($log)){
|
|
|
+ DB::rollBack();
|
|
|
+ RedisService::clear($cacheKey.'_lock');
|
|
|
+ $this->error = '答题失败,请刷新后重新提交';
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ $log = [
|
|
|
+ 'user_id'=> $userId,
|
|
|
+ 'paper_id'=> $paperId,
|
|
|
+ 'score'=> $topicData['score'],
|
|
|
+ 'answer_count'=> 1,
|
|
|
+ 'answer_times'=> $answerTime,
|
|
|
+ 'answer_last_at'=> date('Y-m-d H:i:s'),
|
|
|
+ 'answer_last_id'=> $tid,
|
|
|
+ 'is_submit'=> $isSubmit,
|
|
|
+ 'create_time'=> time()
|
|
|
+ ];
|
|
|
+
|
|
|
+ if(!$rid = $this->model->insertGetId($log)){
|
|
|
+ DB::rollBack();
|
|
|
+ RedisService::clear($cacheKey.'_lock');
|
|
|
+ $this->error = '答题失败,请刷新后重新提交';
|
|
|
+ return false;
|
|
|
|
|
|
}
|
|
|
}
|
|
|
+ // 答题题目数据
|
|
|
+ $topicData['answer_type'] = $submitType;
|
|
|
+ $topicData['answer_log_id'] = $rid;
|
|
|
+
|
|
|
+ if($logId){
|
|
|
+ $topicData['update_time'] = $submitType;
|
|
|
+ ExamAnswerTopicModel::where(['id'=> $logId])->update([$topicData]);
|
|
|
+ }else if (!ExamAnswerTopicModel::insert($topicData)){
|
|
|
+ DB::rollBack();
|
|
|
+ RedisService::clear($cacheKey.'_lock');
|
|
|
+ $this->error = '答题失败,请刷新后重新提交';
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
+ DB::commit();
|
|
|
+ $this->error = '答题成功';
|
|
|
+ RedisService::clear($cacheKey.'_lock');
|
|
|
+ RedisService::keyDel("caches:exams:{$userId}*");
|
|
|
+ RedisService::clear("caches:exams:info_{$rid}");
|
|
|
+ RedisService::keyDel("caches:paper:info_{$userId}:p{$paperId}*");
|
|
|
+ return ['paper_id'=> $paperId,'rid'=>$rid,'tid'=>$tid, 'accurate'=> $topicData['accurate'],'answer'=>$topicData['answer']];
|
|
|
+ }
|
|
|
|
|
|
+ /**
|
|
|
+ * 是否有答题记录数据
|
|
|
+ * @param $rid
|
|
|
+ * @param $tid
|
|
|
+ * @return array|mixed
|
|
|
+ */
|
|
|
+ public function getAnswerCacheTopic($rid, $tid)
|
|
|
+ {
|
|
|
+ $cacheKey = "caches:answers:topic_{$rid}_{$tid}";
|
|
|
+ $data = RedisService::get($cacheKey);
|
|
|
+ if($data){
|
|
|
+ return $data;
|
|
|
+ }
|
|
|
+ $data = ExamAnswerTopicModel::where(['rid'=> $rid,'topic_id'=>$tid,'mark'=>1])->value('id');
|
|
|
+ if($data){
|
|
|
+ RedisService::set($cacheKey, $data, rand(5, 10));
|
|
|
+ }
|
|
|
|
|
|
+ return $data;
|
|
|
+ }
|
|
|
|
|
|
- if($rid){
|
|
|
- $data = [
|
|
|
- ''
|
|
|
- ];
|
|
|
+ /**
|
|
|
+ * 答题分数结果详情
|
|
|
+ * @param $rid
|
|
|
+ * @return array|mixed
|
|
|
+ */
|
|
|
+ public function getInfo($rid)
|
|
|
+ {
|
|
|
+ $cacheKey = "caches:exams:info_{$rid}";
|
|
|
+ $data = RedisService::get($cacheKey);
|
|
|
+ if($data){
|
|
|
+ return $data;
|
|
|
}
|
|
|
|
|
|
+ $data = $this->model->from('exam_answers as a')
|
|
|
+ ->leftJoin('exam_papers as b','b.id','=','a.paper_id')
|
|
|
+ ->where(['a.id'=> $rid,'a.status'=>1,'a.mark'=>1])
|
|
|
+ ->select(['a.*','b.topic_count'])
|
|
|
+ ->first();
|
|
|
+ $data = $data? $data->toArray() : [];
|
|
|
+ if($data){
|
|
|
+ $data['accurate_rate'] = round($data['accurate_count']/$data['topic_count'] * 100,2);
|
|
|
+ $data['answer_times_text'] = $data['answer_times']? format_times($data['answer_times']) : '00:00';
|
|
|
+ RedisService::set($cacheKey, $data, rand(5, 10));
|
|
|
+ }
|
|
|
|
|
|
+ return $data;
|
|
|
}
|
|
|
|
|
|
}
|