$params['user_id'], 'status' => 1])->where('id', $params['job_id'])->select()->toArray(); } else { $result = InterviewJob::where(['user_id' => $params['user_id'], 'status' => 1])->select()->toArray(); } if(!$result){ throw new Exception('岗位不存在!'); } return $result; } /** * @desc 岗位详情 * @param array $params * @return array * @date 2025/2/13 17:29 * @throws Exception * @author dagouzi */ public static function jobDetail(array $params) { $result = InterviewJob::where(['id' => $params['id'], 'status' => 1])->findOrEmpty()->toArray(); if (empty($result)){ throw new Exception('面试不存在或已取消!'); } return $result; } /** * @desc 简历识别 * @return true * @date 2025/2/13 17:29 * @author dagouzi */ public static function extractCv($params) { if(!isset($params['interview_job_id'])){ throw new Exception('参数缺少!'); } $user_id = InterviewJob::where(['id' => $params['interview_job_id']])->value('user_id'); if(!$user_id){ throw new Exception('岗位不存在!'); } // Db::startTrans(); try { //计费 //$unit = TokenLogService::checkToken($user_id, 'interview_cv'); $unit = 0; $url = $params['word']; $urlData = parse_url($url); $path = public_path() . $urlData['path']; $file = new \CURLFile($path); $response = \app\common\service\ToolsService::Interview()->cv([ 'file' => $file, 'action' => 'upload' ]); $file_id = $response['data']['file_id'] ?? ''; if(empty($file_id)){ throw new Exception('简历上传失败!'); } $file_id = 'fileid://' . $file_id; $cv_content = [ "role"=>"简历信息解析助手", "description"=>"从用户上传的简历文本中精准提取结构化信息,并以标准化JSON格式返回。", "instruction"=> '请严格按照以下规则解析简历内容: 1. **提取字段**: - 姓名(需全称,忽略昵称/英文名) - 性别(若未明确标注则留空) - 手机号(若未明确标注则留空) - 年龄(优先取数字格式,若为出生日期则自动计算) - 工作年限(优先取数字格式,若为工作年限则自动计算) - 学历(最高学历,如:博士/硕士/本科) - 毕业院校(最高学历对应院校,合并分校信息) - 工作经历(按倒序排列,格式:["公司名 | 职位 | 时间段(起止年月) | 核心职责摘要"]) - 项目经历(按倒序排列,格式:["项目名称 | 角色 | 技术栈/工具 | 成果量化描述"]) 2. **处理规则**: - 合并分散段落:若同一经历分多段描述,需合并为单一条目 - 清洗冗余词:去除「负责」、「参与」等非必要前缀 - 时间标准化:时间段统一为「YYYY.MM-YYYY.MM」格式 - 量化成果:项目成果需包含可量化的指标(如提升30%/节省100小时) 3. **输出要求**: - 严格使用JSON格式 - 空值字段保留为null - 特殊符号转义处理 , "response_example": { "name": "王小明", "sex": "男", "age": 28, "work_years": 3, "mobile": "13800138000", "degree": "硕士", "school": "清华大学计算机科学与技术系", "work_ex": [ "阿里巴巴集团 | 高级后端开发工程师 | 2020.07-2023.05 | 主导支付系统重构,QPS从5k提升至12k", "字节跳动 | Java开发工程师 | 2018.03-2020.06 | 搭建实时推荐系统,DAU提升15%" ], "project_ex": [ "分布式消息队列优化 | 技术负责人 | Kafka/Go/Prometheus | 降低端到端延迟从200ms至80ms", "智能风控系统 | 核心开发者 | Spring Cloud/Redis/Elasticsearch | 拦截欺诈交易准确率达99.2%" ]' ]; $messages = [ [ "role"=>"system", "content"=> json_encode($cv_content, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), ], [ "role"=>"system", "content"=> $file_id ], [ "role"=>"user", "content"=>"请把这份简历文件给我JSON格式数据" ] ]; $response = \app\common\service\ToolsService::Interview()->jx([ 'messages' => $messages, 'action' => 'qwen' ]); if (empty($response['data']['message'])) { throw new Exception('简历分析失败!'); } $message = $response['data']['message']; $json = format_json($message); $sex = [ '男' => 1, '女' => 2 ]; foreach($json as $key => &$value){ if(is_array($value)){ $value = json_encode($value,JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); } //性别替换 if($key == 'sex'){ $value= $sex[$value] ?? 0; } if(empty($value)){ $value = ''; } } $json['word_url'] = $url ; $json['company_id'] = $user_id; $json['type'] = 2; $json['interview_job_id'] = $params['interview_job_id']; $json['user_id'] = $params['user_id']; // $cvres = InterviewCv::create($json); // if($unit > 0){ // //扣除算力 // User::userTokensChange($user_id, $unit); // $extra = [ // '解析简历数' => 1, // "算力单价" => $unit, // "实际消耗算力" => $unit // ]; // //记录日志 // AccountLogLogic::recordUserTokensLog(true, $user_id, AccountLogEnum::TOKENS_DEC_AI_RESUME, $unit, $cvres->id, $extra); // } // Db::commit(); self::$returnData = $json; return true; } catch (\Exception $e) { Log::error('简历上传失败'.$e->getMessage().'参数'.json_encode($json)); // Db::rollback(); throw new Exception($e->getMessage()); } } public static function formatJson($text) { $text = <<getSections(); $textContent = ''; foreach ($sections as$section) { $elements =$section->getElements(); foreach ($elements as$element) { if ($element instanceof \PhpOffice\PhpWord\Element\TextRun) { $textRunElements =$element->getElements(); foreach ($textRunElements as$textElement) { if ($textElement instanceof \PhpOffice\PhpWord\Element\Text) { $textContent .=$textElement->getText(); } } } } } return $textContent; } catch (\PhpOffice\PhpWord\Exception\Exception $e) { // 捕获异常并输出错误信息 die('Error loading DOCX file: ' . $e->getMessage()); } } /** * @desc 保存简历 * @return true * @date 2025/2/14 11:40 * @author dagouzi */ public static function saveCv(array $params) { if(!isset($params['interview_job_id'])){ throw new Exception('参数缺少!'); } $user_id = InterviewJob::where(['id' => $params['interview_job_id']])->value('user_id'); if(!$user_id){ throw new Exception('岗位不存在!'); } $params['company_id'] = $user_id; $params['interview_job_id'] = $params['interview_job_id']; $cv = InterviewCv::where(['user_id' => $params['user_id'],'interview_job_id'=>$params['interview_job_id']])->findOrEmpty(); foreach($params as $key => &$value){ $value = trim($value); if($key == 'word_url'){ continue; } if($key == 'work_url'){ continue; } if(empty($value)){ throw new Exception('参数不能为空'); } } if(isset($params['work_url'])){ unset($params['work_url']); } if ($cv->isEmpty()) { InterviewCv::create($params); } else { InterviewCv::where(['id' => $cv->id])->save($params); } return true; } /** * @desc 开始面试 * @param array $params * @return true * @date 2025/2/17 10:03 * @author dagouzi */ public static function start(array $params) { Db::startTrans(); // 开始事务 try { $job = InterviewJob::where('id', $params['job_id'])->findOrEmpty()->toArray(); if (empty($job)) { throw new Exception('岗位不存在!'); } if ($job['status'] == 0) { throw new Exception('岗位已关闭!'); } $interviewRecord = InterviewRecord::where(['user_id' => $params['user_id'], 'job_id' => $params['job_id']])->findOrEmpty()->toArray(); if (empty($interviewRecord)) { $interviewCv = InterviewCv::where(['user_id' => $params['user_id'], 'interview_job_id' => $params['job_id']])->findOrEmpty()->toArray(); if (empty($interviewCv)) { throw new Exception('没有简历信息!'); } $params['job_name'] = $job['name']; $params['interview_name'] = $interviewCv['name']; $params['degree'] = $interviewCv['degree']; $params['work_years'] = $interviewCv['work_years']; $params['start_time'] = time(); $params['first_start_time'] = time(); $params['last_end_time'] = $params['first_start_time']; $params['duration'] = 0; $interviewRecord = InterviewRecord::create($params)->toArray(); $unit = TokenLogService::checkToken($job['user_id'], 'interview_chat'); if ($unit > 0) { User::userTokensChange($job['user_id'], $unit); $extra = [ "AI面试次数" => 1, "算力单价" => $unit, "实际消耗算力" => $unit ]; AccountLogLogic::recordUserTokensLog(true, $job['user_id'], AccountLogEnum::TOKENS_DEC_AI_INTERVIEW_CHAT, $unit, $interviewRecord['id'], $extra); } } $interview = Interview::where(['user_id' => $params['user_id'], 'job_id' => $params['job_id']])->order('id', 'desc')->findOrEmpty()->toArray(); //没有面试邀约,生成一个新的 if (empty($interview)) { self::$returnData = self::createInterviewAndDialog($params, $interviewRecord); Db::commit(); // 提交事务 return true; } if ($interview['status'] == Interview::STATUS_RESTART) { self::$returnData = self::createInterviewAndDialog($params, $interviewRecord); InterviewRecord::update(['status' => Interview::STATUS_ONGOING], ['id' => $interviewRecord['id']]); Db::commit(); // 提交事务 return true; } if (empty($interviewRecord)) { throw new \Exception('没有面试记录'); } if ($interview['status'] == Interview::STATUS_COMPLETED) { $interview['msg'] = '面试已结束'; self::$returnData = $interview; return true; } if ($interview['status'] == Interview::STATUS_ONGOING) { $interview['msg'] = '有个面试正在进行中'; self::$returnData = $interview; return true; } //面试退出或者中断 if (in_array($interview['status'], [Interview::STATUS_EXITED, Interview::STATUS_INTERRUPTED])) { $params['start_time'] = time(); $params['interview_record_id'] = $interviewRecord['id']; $interviewnew = Interview::create($params)->toArray(); $interviewnew['last_interview_id'] = $interview['id']; $interviewnew['prologue'] = '好,很高兴您能来参加本轮面试,我是你的AI面试官,先请您先做一个简单的自我介绍吧。!!'; self::$returnData = $interviewnew; InterviewRecord::update(['status' => Interview::STATUS_ONGOING], ['id' => $interviewRecord['id']]); Db::commit(); // 提交事务 return true; } if ($interview['status'] == Interview::STATUS_RESTART) { $params['start_time'] = time(); $params['interview_record_id'] = $interviewRecord['id']; $interviewnew = Interview::create($params)->toArray(); $interviewnew['prologue'] = '您好,很高兴您能来参加本轮面试,我是你的AI面试官,先请您先做一个简单的自我介绍吧。!'; self::$returnData = $interviewnew; Db::commit(); // 提交事务 return true; } throw new \Exception('数据错误'); } catch (\Exception $e) { Db::rollback(); // 回滚事务 throw new Exception($e->getMessage()); } } public static function chat($params) { $user_id = $params['user_id']; $interview_id = $params['id']; $isEnd = 0; $endMessgae = '好的,大致情况我已经了解,本轮面试已结束,感谢您的配合,请提交面试过程并耐心等待通知。'; $interview = Interview::where(['id' => $interview_id, 'user_id' => $user_id, 'status' => 0])->findOrEmpty(); if ($interview->isEmpty()) { throw new Exception('没有面试信息或已结束!'); } $interviewRecord = InterviewRecord::where(['id' => $interview->interview_record_id, 'user_id' => $user_id]) ->whereIn('status', [0, 1])->findOrEmpty(); if ($interviewRecord->isEmpty()) { throw new Exception('没有面试信息或已结束!'); } $job = InterviewJob::findOrEmpty($interview->job_id); if ($job->isEmpty()) { throw new Exception('没有岗位信息!'); } // 1:文字 2:语音 $dialogType = $job->type; $cv = InterviewCv::where(['user_id' => $user_id ,'interview_job_id' => $interview->job_id])->findOrEmpty(); if ($cv->isEmpty()) { throw new Exception('没有简历信息!'); } // 修改对话的回复内容 $curDialog = InterviewDialog::where(['interview_id' => $interview_id])->order('id DESC')->findOrEmpty(); // 对话记录条数 $dialogCount = InterviewDialog::where(['interview_id' => $interview_id])->count(); if ($dialogType == 2) { $answer = self::stt($params['answer_url']); $curDialog->answer = $answer['message']; $curDialog->answer_url = $params['answer_url']; $curDialog->answer_duration = $answer['audio_duration']; } else { $curDialog->answer = $params['answer']; } $curDialog->save(); // 判断当前对话类型 $attentionArray = json_decode($job->attention, true); $attentionCount = count($attentionArray); // 关注点个数 // 检测是否已结束 $chatTypeEnd = self::chatType($dialogCount + 1, $attentionCount); if ($chatTypeEnd == 0) { // 访问通义获取评分和面试评价 Queue::push('app\common\Jobs\EndInterviewJob@handle', $interview->id); $interview->end_time = time(); $interview->status = Interview::STATUS_ANALYZE; $interview->save(); $duration = $interview->end_time - $interviewRecord->first_start_time; $interviewRecord->duration = $duration; $interviewRecord->end_time = $interview->end_time; $interviewRecord->status = Interview::STATUS_ANALYZE; $interviewRecord->last_interview_id = $interview->id; $interviewRecord->save(); $message = $endMessgae = '好的,大致情况我已经了解,本轮面试已结束,感谢您的配合,请提交面试过程并耐心等待通知。'; self::$returnData = [ 'id' => $interview_id, 'status' => 1, 'end_message' => $endMessgae, 'message' => $message ]; return true; } $chatType = self::chatType($dialogCount, $attentionCount); // 1:大问题机器人 2:深入问题机器人 3:不带关注点的大问题机器人 $message = $audio_url = ''; $audio_duration = 0; if ($chatType == 1) { $dialogs = InterviewDialog::where('type', 'in', [1,4])->where('interview_id', $interview_id)->select()->toArray(); $message = self::chat1($job, $cv, $dialogs); if (!empty($message)) { $audioData = self::tts($message); $audio_url = $dialogType == 2 ? $audioData['audio_url'] : ''; $audio_duration = $audioData['audio_duration']; // 新增对话记录 InterviewDialog::create([ 'interview_id' => $interview_id, 'type' => 1, 'question' => $message, 'question_url' => $audio_url, 'question_duration' => $audio_duration ]); } } elseif($chatType == 2) { $mainDialog = InterviewDialog ::where('type', 'in', [1,3]) ->where('interview_id', $interview_id) ->order('id DESC') ->findOrEmpty(); $dialogs = InterviewDialog::where('id', '>=', $mainDialog->id)->where('interview_id', $interview_id)->select()->toArray(); $message = self::chat2($job, $cv, $dialogs); if (!empty($message)) { $audioData = self::tts($message); $audio_url = $dialogType == 2 ? $audioData['audio_url'] : ''; $audio_duration = $audioData['audio_duration']; // 新增对话记录 InterviewDialog::create([ 'interview_id' => $interview_id, 'type' => 2, 'question' => $message, 'question_url' => $audio_url, 'question_duration' => $audio_duration ]); } } elseif($chatType == 3) { $dialogs = InterviewDialog::where('type', 'in', [1,3])->where('interview_id', $interview_id)->select()->toArray(); $message = self::chat3($job, $cv, $dialogs); if (!empty($message)) { $audioData = self::tts($message); $audio_url = $dialogType == 2 ? $audioData['audio_url'] : ''; $audio_duration = $audioData['audio_duration']; // 新增对话记录 InterviewDialog::create([ 'interview_id' => $interview_id, 'type' => 3, 'question' => $message, 'question_url' => $audio_url, 'question_duration' => $audio_duration ]); } } else { // 结束了 throw new Exception('面试已结束!'); } self::$returnData = [ 'id' => $interview_id, 'status' => $interview->status, 'end_message' => $endMessgae, 'message' => $message, 'audio_url' => $audio_url, 'audio_duration' => $audio_duration ]; return true; } /** * @desc 判断当前对话类型 * @param $dialogs * @return int * @date 2025/3/4 18:15 * @author dagouzi */ public static function chatType($dialogCount, $attentionCount, $deepTimes = 3) { $dialogCount = $dialogCount - 1; $deepStep = $deepTimes + 1; $total = $deepStep * $attentionCount + 4 * 5; if ($dialogCount - $total > -1) { return 0; } if ($dialogCount % $deepStep == 0) { return $dialogCount / $deepStep < $attentionCount ? 1 : 3; } else { return 2; } } /** * @desc 文字转语音 * @return array * @date 2025/2/19 11:26 * @author dagouzi */ public static function tts($text) { $response = \app\common\service\ToolsService::Interview()->chat([ 'message' => $text, 'action' => 'tts' ]); if (empty($response) || empty($response['code'])) { throw new Exception('语音转文字失败~'); } if ($response['code'] == 10000) { return $response['data']; } throw new Exception('语音转文字失败~'); } /** * @desc 语音转文字 * @return array * @date 2025/2/19 11:27 * @author dagouzi */ public static function stt($voice) { $response = \app\common\service\ToolsService::Interview()->chat([ 'action' => 'stt', 'audio_url' => $voice ]); if (empty($response) || empty($response['code'])) { throw new Exception('语音转文字失败~'); } if ($response['code'] == 10000) { return $response['data']; } throw new Exception('语音转文字失败~'); } /** * @desc 大问题(带关注点) * @param $job * @param $cv * @param $sex * @param $attention * @return mixed * @date 2025/2/18 10:09 * @author dagouzi */ public static function chat1($job, $cv, $dialogs) { $sex = $cv->sex == 2 ? '女' : '男'; $attention = implode(";", json_decode($job->attention, true)); $system = " # 角色定位 专业HR面试官,负责根据候选者的简历信息和HR关注点生成结构化面试问题。 # 核心任务 基于以下规则生成1个面试问题: 1. 输入要素: 岗位:{$job->name} 主要职责:{$job->desc} 任职要求:{$job->jd} 附加考察要求: {$job->extra} 候选人简历信息: 姓名:{$cv->name} 性别:{$sex} 年龄:{$cv->age} 学历:{$cv->degree} 毕业院校:{$cv->school} 工作经历:{$cv->work_ex} 项目经历:{$cv->project_ex} HR关注点: {$attention} 2. 输出要求: • 问题需覆盖技能、经验、行为等不同维度 • 避免与已生成问题重复 • 仅输出问题,不加编号或解释 • 要把自己当成面试官,要把自己当成HR • 不要把自己当成AI面试者,不要把自己当成AI候选人 # 示例模板 输入: HR关注点:团队协作与抗压能力 输出: 请描述一次你因资源不足而调整原计划的经历。 "; $message[] = [ 'role' => 'system', 'content' => $system ]; foreach ($dialogs as $dialog) { $message[] = [ 'role' => 'assistant', 'content' => $dialog['question'] ]; $message[] = [ 'role' => 'user', 'content' => $dialog['answer'] ]; } $response = \app\common\service\ToolsService::Interview()->chat([ 'action' => 'chat', 'messages' => $message ]); if ($response['code'] == 10000 && !empty($response['data']['message'])) { return $response['data']['message']; } else { throw new Exception('对话错误~'); } } public static function chat2($job, $cv, $dialogs) { $sex = $cv->sex == 2 ? '女' : '男'; $curDialog = reset($dialogs); $system = " # 角色定位 深度追问面试官,负责根据候选人回答递进挖掘细节。 # 核心任务 基于以下规则生成1个面试问题: 1. 输入要素: 岗位:{$job->name} 主要职责:{$job->desc} 任职要求:{$job->jd} 附加考察要求: {$job->extra} 候选人简历信息: 姓名:{$cv->name} 性别:{$sex} 年龄:{$cv->age} 学历:{$cv->degree} 毕业院校:{$cv->school} 工作经历:{$cv->work_ex} 项目经历:{$cv->project_ex} 当前问题: {$curDialog['question']} 2. 输出要求: • 问题需覆盖技能、经验、行为等不同维度 • 避免与已生成问题重复 • 仅输出问题,不加编号或解释 # 示例模板 输入: HR关注点:团队协作与抗压能力 输出: 请描述一次你因资源不足而调整原计划的经历。 "; $message[] = [ 'role' => 'system', 'content' => $system ]; foreach ($dialogs as $dialog) { $message[] = [ 'role' => 'assistant', 'content' => $dialog['question'] ]; $message[] = [ 'role' => 'user', 'content' => $dialog['answer'] ]; } ; $response = \app\common\service\ToolsService::Interview()->chat([ 'action' => 'chat', 'messages' => $message ]); if ($response['code'] == 10000 && !empty($response['data']['message'])) { return $response['data']['message']; } else { throw new Exception('对话错误~'); } } public static function chat3($job, $cv, $dialogs) { $sex = $cv->sex == 2 ? '女' : '男'; $system = " # 角色定位 AI深度面试官,负责基于整体表现提出针对性问题。 # 核心任务 基于以下规则生成1个面试问题: 1. 输入要素: 岗位:{$job->name} 主要职责:{$job->desc} 任职要求:{$job->jd} 附加考察要求: {$job->extra} 候选人简历信息: 姓名:{$cv->name} 性别:{$sex} 年龄:{$cv->age} 学历:{$cv->degree} 毕业院校:{$cv->school} 工作经历:{$cv->work_ex} 项目经历:{$cv->project_ex} 2. 输出要求: • 问题需覆盖技能、经验、行为等不同维度 • 避免与已生成问题重复 • 仅输出问题,不加编号或解释 # 示例模板 输入: HR关注点:团队协作与抗压能力 输出: 请描述一次你因资源不足而调整原计划的经历。 "; $message[] = [ 'role' => 'system', 'content' => $system ]; foreach ($dialogs as $dialog) { $message[] = [ 'role' => 'assistant', 'content' => $dialog['question'] ]; $message[] = [ 'role' => 'user', 'content' => $dialog['answer'] ]; } $response = \app\common\service\ToolsService::Interview()->chat([ 'action' => 'chat', 'messages' => $message ]); if ($response['code'] == 10000 && !empty($response['data']['message'])) { return $response['data']['message']; } else { throw new Exception('对话错误~'); } } /** * @desc 深入问题机器人 * @param $dialogData * @return mixed * @date 2025/2/18 10:57 * @author dagouzi */ public static function qwen($dialogs) { $qwenData = []; foreach ($dialogs as $item) { $qwenData[] = [ 'question' => $item['question'], 'answer' => $item['answer'], ]; } $messages = [ [ 'role' => 'system', 'content' => '{ "role": "面试分析助手", "description": "你是一位专业的面试分析助手,专注于分析完整面试对话历史,并为本次面试提供总分(区间为1-100分)和详细评价,评价需公正客观且详细具体。", "interaction": { "instruction": "请根据提供的面试对话文本,为本次面试打分(总分区间为1-100分),并提供一段详细评价,评价需涵盖问题设计、追问深度、候选人表现、逻辑连贯性和整体效果等方面,且评价内容需公正客观、具体详细。", "scene_name": "AI面试总分与评价", "dialogue_text": "【面试对话内容】", "response_format": "JSON", "response_format_example": { "total_score": 0, "detailed_evaluation": "" } } }' ], [ 'role' => 'user', 'content' => "对话记录:" . json_encode($qwenData, JSON_UNESCAPED_UNICODE) ] ]; $response = \app\common\service\ToolsService::Interview()->chat([ 'action' => 'qwen', 'messages' => $messages ]); if (empty($response['data']['message'])) { throw new Exception('评分失败~'); } $result = format_json($response['data']['message']); if (empty($result['total_score']) || empty($result['detailed_evaluation'])) { throw new Exception('评分失败~'); } return $result; } public static function feedback(array $params) { InterviewFeedback::create($params); return true; } public static function getStt(array $params) { $message = self::stt($params['audio_url']); self::$returnData = ['message' => $message['message']]; return true; } public static function checkInterview(array $params) { $interview = Interview::where(['user_id' => $params['user_id'], 'job_id' => $params['job_id']]) ->whereIn('status', [2,3,4])->count(); if($interview > 3){ $data['type'] = 7; $data['msg'] = '当前面试已经超过三次!'; self::$returnData = $data; return true; } $job = InterviewJob::where(['id' => $params['job_id']])->findOrEmpty()->toArray(); if (empty($job)) { $data['type'] = 0; $data['msg'] = '岗位不存在'; self::$returnData = $data; return true; } $userInfo = User::findOrEmpty($job['user_id'])->toArray(); // if (empty($userInfo)) { // $data['type'] = 8; // $data['msg'] = '用户查询失败'; // self::$returnData = $data; // return true; // } $use_token = ModelConfig::where('scene', 'interview_chat')->value('score', 0); if ($userInfo['tokens'] < $use_token) { $data['type'] = 9; $data['msg'] = '当前岗位可用算力不足,请联系面试官!'; self::$returnData = $data; return true; } $data['type'] = 0; try { $interviewRecord = InterviewRecord::where(['user_id' => $params['user_id'], 'job_id' => $params['job_id']]) ->order('id', 'desc') ->findOrEmpty() ->toArray(); if (empty($interviewRecord)) { // 检查是否上传了简历 $interviewCv = InterviewCv::where(['user_id' => $params['user_id'],'interview_job_id' => $params['job_id']]) ->order('id', 'desc') ->findOrEmpty() ->toArray(); if (empty($interviewCv)) { $data['type'] = 1; $data['msg'] = '没有上传简历'; self::$returnData = $data; return true; } $data['type'] = 2; $data['msg'] = '没有面试记录'; self::$returnData = $data; return true; } $interview = Interview::where(['user_id' => $params['user_id'], 'job_id' => $params['job_id']]) ->order('id', 'desc') ->findOrEmpty() ->toArray(); if (empty($interview)) { $data['type'] = 5; $data['msg'] = '面试中断'; self::$returnData = $data; return true; } if ($interview['status'] == 3) { $data['type'] = 4; $data['msg'] = '面试重新开始'; $data['id'] = $interview['id']; $data['status'] = $interview['status']; self::$returnData = $data; return true; } if ($interview['status'] == 1) { $data['type'] = 6; $data['msg'] = '面试已完成'; self::$returnData = $data; return true; } $data['msg'] = '上一轮面试,还没有面试完!!'; $data['id'] = $interview['id']; $data['status'] = $interview['status']; $data['type'] = 3; self::$returnData = $data; return true; } catch (\Exception $e) { // 捕获异常并设置错误信息 throw new \Exception('系统错误: ' . $e->getMessage()); return false; } } // 创建面试和插入对话开场白的通用方法 private static function createInterviewAndDialog(array $params, array $interviewRecord): array { $params['start_time'] = time(); $params['interview_record_id'] = $interviewRecord['id']; // 创建面试 $interview = Interview::create($params)->toArray(); $interview['prologue'] = '您好,很高兴您能来参加本轮面试,我是你的AI面试官,先请您先做一个简单的自我介绍吧。'; // 获取岗位信息 $job = InterviewJob::where('id', $params['job_id'])->findOrEmpty()->toArray(); if ($job['type'] == 2) { $audioData = self::tts($interview['prologue']); $interview['audio_url'] = $audioData['audio_url']; $interview['audio_duration'] = $audioData['audio_duration']; } // 插入对话开场白 InterviewDialog::create([ 'interview_id' => $interview['id'], 'type' => 4, 'question' => $interview['prologue'], 'question_url' => $interview['audio_url'] ?? '', 'question_duration' => $interview['audio_duration'] ?? 0, ]); return $interview; } }