| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693 | <?phpnamespace addons\exam\controller;use addons\exam\enum\CommonStatus;use addons\exam\enum\ExamMode;use addons\exam\library\ExamService;use addons\exam\model\PaperModel;use addons\exam\model\QuestionModel;use addons\exam\model\UserModel;use app\admin\model\exam\CateModel;use app\admin\model\exam\GradeModel;use think\Request;use think\Db;include_once EXTEND_PATH.'icbc/DefaultIcbcClient.php';/** * 试卷接口 */class Paper extends Base{    protected $noNeedLogin = ['index','lists','detail'];    protected $noNeedRight = ['*'];    protected $user;    /**     * 查询出分类下的试卷     */    public function index()    {        $cate_id = input('cate_id/d', '0');        $sort    = input('sort/s', '');        $now     = time();        $query = PaperModel::with([            'cates' => function ($query) {                $query->withField('id, name');            }        ])            ->where('status', CommonStatus::NORMAL)            ->where('is_only_room', 0)// 过滤仅考场使用的试卷            ->whereRaw("((start_time = 0 and end_time = 0) or (start_time < {$now} and end_time > {$now}))");        // 分类        if ($cate_id) {            $child_cate_ids = CateModel::getChildId($cate_id);            array_push($child_cate_ids, $cate_id);            $query->whereIn('cate_id', $child_cate_ids);        }        // 排序        if ($sort && $sort != 'null') {            $sort     = explode('|', $sort);            $field    = $sort[0];            $order_by = $sort[1];            $field    = in_array($field, ['join_count']) ? $field : 'join_count';            $order_by = $order_by == 'desc' ? 'desc' : 'asc';            $query->order("{$field} $order_by");        }        $list = $query->paginate();        $this->success('', ['list' => $list]);    }    public function lists()    {        $list = Db::name('exam_paper')->field('id,image,title,updatetime')->where('status','normal')            ->order('weigh desc, id desc')->autopage()->select();        $list = list_domain_image($list,['image']);        $this->success('', $list);    }    public function detail(){        $id = input('id');        $info = Db::name('exam_paper')->field('id,image,title,content,updatetime,quantity')->where('status','normal')            ->find($id);        $info = info_domain_image($info,['image']);        $this->success('', $info);    }    /**     * 试卷取题接口     */    public function getExamQuestion()    {        $paper_id = input('paper_id/d', 0);        $room_id  = input('room_id/d', 0);        // 验证是否需要绑定手机号//        UserModel::isMustBindMobile($this->auth->getUser());        // 预创建考场考试记录//        $room_grade_id = ExamService::preRoomGrade($room_id, $this->auth->id);        // 获取试卷题目        $question_data = ExamService::getExamQuestion($paper_id, $room_id);//        $this->success('', array_merge($question_data, ['room_grade_id' => $room_grade_id]));        $this->success('', $question_data);    }    //开始考试接口    public function startpaper(){        $paper_id      = input('paper_id/d', 0);        $user_id = $this->auth->id;        //检查考试状态        /*$check = Db::name('exam_grade')->where('user_id', $user_id)->where('status',1)->find();        if($check){            $this->success('您有其他考试正在进行中,即将继续考试',0);//直接给成功,数据返回0,前端跳转        }*/        //检查试卷        $paper   = PaperModel::get($paper_id);        switch (true) {            case !$paper:                $this->error('科普答题信息不存在');            case $paper->status != 'NORMAL':                $this->error('科普答题信息不存在');            case $paper->mode == 'RANDOM' && !$paper->configs:                $this->error('科普答题信息未配置');        }        //时间限制        /*if ($paper['start_time'] > 0 && $paper['start_time'] > time()) {            $this->error('该试卷未开始,不能参与考试');        }        if ($paper['end_time'] > 0 && $paper['end_time'] < time()) {            $this->error('该试卷已结束,不能参与考试');        }*/        //考试资格        /*if(!in_array($user_id,explode(',',$paper['user_ids']))){            $this->error('您不能参加该考试');        }*/        //次数限制        if ($paper['limit_count'] > 0){            $my_count = Db::name('exam_grade')->where('user_id', $user_id)->where('paper_id', $paper_id)->where('status',2)->count();            if($my_count >= $paper['limit_count']) {                $this->error('在该科普中您的答题次数已达上限');            }        }        //记录为已开始,计划任务倒计时之后 自动结束        $data = [            'cate_id'  => $paper['cate_id'],            'user_id'  => $this->auth->id,            'paper_id' => $paper_id,            'mode'     => $paper['mode'],            'total_score' => $paper['total_score'],            'total_count' => $paper['quantity'],            'start_time' => time(),            'createtime' => time(),            'status' => 1,            'limit_time' => $paper['limit_time'],  //限时N秒            'last_time' => $paper['limit_time'] > 0 ? (time() + $paper['limit_time']) : 0, //最后限制交卷时间,时间戳        ];        $grade_id = Db::name('exam_grade')->insertGetId($data);        $this->success('',$grade_id);    }    //进行中考试    public function half_examing(){        $user_id = $this->auth->id;        $check = Db::name('exam_grade')->where('user_id', $user_id)->where('status',1)->find();        if(empty($check)){            $this->error('您没有进行中的考试');        }        $paper_id = $check['paper_id'];        // 获取试卷题目        $question_data = ExamService::getExamQuestion($paper_id, 0);        $question_data['paper']['limit_time'] = $check['last_time'] - time();//倒计时秒数        $this->success('', $question_data);    }    /**     * 交卷     */    public function submit()    {        $request       = Request::instance();        $user_id       = $this->auth->id;        $grade_id      = $request->post('grade_id');        $paper_id      = $request->post('paper_id/d', 0);        $questions     = $request->post('questions/a', []);        $start_time    = $request->post('start_time/d', time());        $room_id       = 0;        $room_grade_id = $request->post('room_grade_id/d', 0);              if (!$user_id || !$paper_id || !$questions) {            $this->error('提交数据有误');        }        $check = Db::name('exam_grade')->where('status',1)->where('id',$grade_id)->where('user_id',$user_id)->where('paper_id',$paper_id)->find();        if(!$check){            $this->error('交卷有误,或者您已交卷');        }        $start_time = $check['start_time'];        // 考场考试        if ($room_id) {            if (!$room_grade_id) {                $this->error('提交数据不合法');            }            // 考场考试            $result = ExamService::roomExam($user_id, $room_id, $room_grade_id, $questions, $start_time, $paper, $room, $is_makeup, $room_grade_log);            // 记录考场考试成绩            $room_grade_log->allowField(true)->save(                array_merge(                    $result,                    [                        // 'cate_id'   => $paper['cate_id'],                        'user_id'   => $user_id,                        'paper_id'  => $paper_id,                        'is_makeup' => $is_makeup,                        'is_pre'    => 0, // 提交成绩后不再为预创建标记                    ],                    [                        'exam_mode' => ExamMode::ROOM,                    ]                )            );        } else {            $result = ExamService::paperExam($user_id, $paper_id, $questions, $start_time, $paper);            // 记录考试成绩            /*GradeModel::create(array_merge(                $result,                [                    'cate_id'  => $paper['cate_id'],                    'user_id'  => $user_id,                    'paper_id' => $paper_id,                ],                [                    // 'exam_mode' => ExamMode::PAPER,                    'date' => date('Y-m-d'),                ]), true);*/            $update = array_merge(                $result,                [                    'updatetime' => time(),                    'date' => date('Y-m-d'),                    'status' => 2,                    'finish_time' => time(),                ]);            unset($update['pass_score']);            unset($update['start_time']);            $rs = Db::name('exam_grade')->where('id',$grade_id)->update($update);            if($rs === false){                $this->error('交卷失败');            }                        // 异步推送积分到工行(写入队列)            $cephone = '15388010006';            //$cephone = $this->auth->mobile;            $this->addIcbcQueue($cephone, $result['score'], '10938', $this->auth->nickname);                    }        $result['nickname'] = $this->auth->nickname;        unset($result['question_ids']);        unset($result['user_answers']);        unset($result['configs']);        $this->success('',$result);    }    /**     * 积分维护接口 - 推送积分数据到工行     * @param string $mobile_phone 用户手机号     * @param int $integral_value 变动的积分值(整数)     * @param string $integral_type 积分类型     * @param array $remarks 备用字段数组(可选,最多7个)     * @return array 返回接口响应结果     */    public function update_villager_integral($mobile_phone = '', $integral_value = 0, $integral_type = '', $remarks = [])    {        // PHP 8+ 兼容:确保参数不为null        $mobile_phone = $mobile_phone ?? '';        $integral_value = $integral_value ?? 0;        $integral_type = $integral_type ?? '';        $remarks = $remarks ?? [];                // 如果没有传入手机号,尝试从登录用户获取        if (empty($mobile_phone) && isset($this->auth->mobile)) {            $mobile_phone = $this->auth->mobile;        }        // 生成16位唯一序列号(时间戳10位+随机数6位)        $fSeqNo = time() . str_pad(mt_rand(0, 999999), 6, '0', STR_PAD_LEFT);                // 构建业务参数        $biz_content = [            'fSeqNo' => $fSeqNo,            'corpCode' => 'xingfulishequ',            'mobilePhone' => $mobile_phone,            'integralValue' => (string)$integral_value, // 确保是字符串类型            'integralType' => $integral_type,        ];        // 添加备用字段(如果有)        for ($i = 1; $i <= 7; $i++) {            if (isset($remarks[$i - 1]) && !empty($remarks[$i - 1])) {                $biz_content['remark' . $i] = $remarks[$i - 1];            }        }        // 工行RSA密钥(字符串格式)        $public_key = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwFgHD4kzEVPdOj03ctKM7KV+16bWZ5BMNgvEeuEQwfQYkRVwI9HFOGkwNTMn5hiJXHnlXYCX+zp5r6R52MY0O7BsTCLT7aHaxsANsvI9ABGx3OaTVlPB59M6GPbJh0uXvio0m1r/lTW3Z60RU6Q3oid/rNhP3CiNgg0W6O3AGqwIDAQAB';        $private_key = 'MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCR8/ZvKPAdZzsyvapySvztQm56s1N59ynKMOWpUbK4c5MNWMl+q3dvsp+UiSAx2TAeGkYfW3W6nO/3Y0hAZki99fmuzpPujEeHhs79HNwGZYQjN71Vck2JeflTq8jpL+9/up0Kz2nbwtZDMKTTEgbfNeO24gV1bmvD2kQ9j66RIyuXSDwQbVbQfl6LiqKoJm3rbtsbwX1Ytc0/Szslyor1VdPZWnHDMm3m78Hqu7X3vL6K7fAW/4FVbeKV7vWjvyiTQfETmuADqMdsqV5YeqUZWE/Dnzg+6JV/3L9nJ8f+/mPlh8k1h1oW035GrADKFKf1M2ujKKeHICnj7qKCdBeNAgMBAAECggEAHkh+U2YtHAy1Tbvox7ojbJ8iCTd4FJBiDV/D5zPaX0crtdM8S5oMOBLZ5ZnmIjGsODK/ZfY2ITg62huxfBs88J0+5zRZoV9d4BLqk74PMQyTNDN2h2omCGZUgzXbg/a8PMZdm0aZ8k0k4+AN8vWEk3+89c9Dzq/QkFyTWCqdz+Mp9NkcOjA255kj5/D1q9Zj0x9VcmKg3oTtrKL/dkspUxVaMKXcpo6J4AszC05tT3N0SNLhcq51I6B9QdbYsYCkP9whPNYIl/y4dN4QnNEivQzA5/ltr5DDQZc7Xke1+SpJN+ylBOBJ8yvNoDnuxx0xEWuNJ5bUzyri/DNjZRKNQQKBgQDKkxn5R4Q3rChI/KVIXHMecTs9fXN5pbnA8UKU6ZeTl+wHffxwRieHZJJp1nDcQ5i8YXCtqEJcJGFn3T0rEjVCJ/RVHclFO+TjYaK9HtyNJLPrvKmAzjCzV1yPfe4qmpgJddzZI7Vii2uFgklixvhkoY74hwSJtvLPTO0p0xrYdQKBgQC4cgfpCMLYlaHQGz+dAEf4IewQpwBGn7XShCpiXGFciGZZEIzvJMzXP7yo8pDMHQbB4kQsTRRG2fcdHGWI5VQGHPqG5O0tUueKGUlSg7j8Y/Pp8ZrBGSrlilAf17I/u9MC7Xe2ZRVGNgPDYAyjMEGmClI0n2+aN6b4CFVBjYmfuQKBgEKO9KDIE7QrF41rnW7aGWTuNVWty2wzvIWdf4/n9EqlRwLrLS9CjahZrhWiRLDKcPusVFZqi2s09OAoe/mT4PXcpNX2lHPwCvN+1/allje10HvrIBJXLP8v/BSVftR2uO+azzZ1GhrHzksulKgk0eZWguA7lI0fFEZycxYj65UlAoGAD9p1RZlkLfuGgf2llRgOF4zK3o+MHYXiuep0PioUkECFE4ixpGh0Vtf6nkbjHTgteYK6O1iQsppPfCgRrheQBkp9WhTZMfkbP6p2u+nof4ET2PrUQ16naj1eL655erLpKypADORZVMSVxDhAPdKLAfuHH1DI5ed8qXsF4PGKb7kCgYEAm/d+daT6YsbHDZlJ/J9Q8rRkKmiqj43NGQSHKg6Z6BEDibm8wRmj3Itu1N6XVChuaH+ekJzvUnZ/q1nyYzGvy6bOHYn3ziF9aH7wuhcRZ4qARmKDnzTBLg2QXBK1+400O3LJ+sAH/yuH/Y4hzRE6YMxBQpdYfnlJcIFVimAsT1w=';        // 构建通用请求参数        $data = [            'app_id' => '10000000000004096993',            'msg_id' => createUniqueNo('msg', time()),            'format' => 'json',            'charset' => 'UTF-8',            'encrypt_type' => 'AES',            'sign_type' => 'RSA2',        ];        // 记录请求日志(调试用)        \think\Log::record('积分维护接口请求参数: ' . json_encode([            'app_id' => $data['app_id'],            'msg_id' => $data['msg_id'],            'biz_content' => $biz_content        ], JSON_UNESCAPED_UNICODE), 'info');        try {            // 检查必要的类和函数            if (!class_exists('\DefaultIcbcClient')) {                \think\Log::record('DefaultIcbcClient 类不存在', 'error');                return [                    'return_code' => '-98',                    'return_msg' => 'DefaultIcbcClient 类不存在',                    'raw_response' => ''                ];            }                        if (!function_exists('createUniqueNo')) {                \think\Log::record('createUniqueNo 函数不存在', 'error');                return [                    'return_code' => '-97',                    'return_msg' => 'createUniqueNo 函数不存在',                    'raw_response' => ''                ];            }                        // 创建工行客户端            \think\Log::record('准备创建 DefaultIcbcClient...', 'info');            $client = new \DefaultIcbcClient(                $data['app_id'],                $private_key,                $data['sign_type'],                $data['charset'],                $data['format'],                $public_key,                '', '', '', ''            );            \think\Log::record('DefaultIcbcClient 创建成功', 'info');            // 构建请求参数            $request = [                'serviceUrl' => 'https://gw.dccnet.com.cn:8084/api/mybank/farm/farmplatf/updateVillagerIntegral/V1',                'method' => 'POST',                'isNeedEncrypt' => false,                'extraParams' => null,                'biz_content' => $biz_content,            ];            // 发送请求            $response = $client->execute($request, $data['msg_id'], '');                        // 记录原始响应(调试用)            \think\Log::record('积分维护接口原始响应长度: ' . strlen($response), 'info');                        // 检查响应是否为空            if (empty($response)) {                $resultData = [                    'return_code' => '-1',                    'return_msg' => '接口无响应',                    'raw_response' => $response                ];                $this->writeIcbcLog(false, $resultData, $biz_content);                return $resultData;            }                        // 清理响应内容:提取 JSON 部分            // 工行接口可能返回调试信息,需要清理            $clean_response = $this->cleanResponse($response);                        // 如果清理后的响应与原始不同,记录            if ($clean_response !== $response) {                \think\Log::record('积分维护接口响应包含非JSON内容,已清理', 'info');            }                        // 解析响应            $result = json_decode($clean_response, true);                        // 检查JSON解析是否成功            if (json_last_error() !== JSON_ERROR_NONE) {                \think\Log::record('积分维护接口JSON解析失败: ' . json_last_error_msg(), 'error');                $resultData = [                    'return_code' => '-2',                    'return_msg' => 'JSON解析失败: ' . json_last_error_msg(),                    'raw_response' => $response,                    'clean_response' => $clean_response                ];                $this->writeIcbcLog(false, $resultData, $biz_content);                return $resultData;            }                        // 检查是否有response_biz_content(按API文档,响应数据在这个字段里)            if (isset($result['response_biz_content'])) {                $biz_result = json_decode($result['response_biz_content'], true);                if ($biz_result) {                    // 合并业务响应和原始响应                    $resultData = array_merge($biz_result, ['raw_response' => $response]);                    $this->writeIcbcLog($resultData['return_code'] == 0, $resultData, $biz_content);                    return $resultData;                }            }                        // 如果直接有return_code,直接返回            if (isset($result['return_code'])) {                $this->writeIcbcLog($result['return_code'] == 0, $result, $biz_content);                return $result;            }                        // 返回完整结果            $resultData = $result ? $result : [                'return_code' => '-3',                'return_msg' => '未知响应格式',                'raw_response' => $response            ];            $this->writeIcbcLog(false, $resultData, $biz_content);            return $resultData;                    } catch (\Exception $e) {            // 捕获异常            \think\Log::record('积分维护接口异常: ' . $e->getMessage(), 'error');            $resultData = [                'return_code' => '-99',                'return_msg' => '接口调用异常: ' . $e->getMessage(),                'raw_response' => ''            ];            $this->writeIcbcLog(false, $resultData, $biz_content);            return $resultData;        }    }    /**     * 添加工行积分任务到队列     * @param string $mobile_phone 手机号     * @param int $integral_value 积分值     * @param string $integral_type 积分类型     * @param string $nickname 用户昵称     */    private function addIcbcQueue($mobile_phone, $integral_value, $integral_type, $nickname)    {        try {            // PHP 8+ 兼容:确保所有值都不为null            $data = [                'mobile_phone' => $mobile_phone ?? '',                'integral_value' => $integral_value ?? 0,                'integral_type' => $integral_type ?? '',                'nickname' => $nickname ?? '',                'status' => 0, // 0-待处理, 1-处理中, 2-成功, 3-失败                'retry_count' => 0,                'createtime' => time(),                'updatetime' => time(),            ];                        Db::name('icbc_queue')->insert($data);                        // 不立即处理,完全由定时任务处理            // 这样可以避免 fastcgi_finish_request() 影响正常响应                    } catch (\Exception $e) {            // 队列写入失败,记录日志但不影响主流程            \think\Log::record('工行队列写入失败: ' . $e->getMessage(), 'error');        }    }    /**     * 异步处理工行队列(不阻塞当前请求)     */    private function processIcbcQueueAsync()    {        // 使用fastcgi_finish_request()立即返回响应给用户        if (function_exists('fastcgi_finish_request')) {            fastcgi_finish_request();        }                // 后台处理队列        $this->processIcbcQueue();    }    /**     * 处理工行队列任务     */    public function processIcbcQueue()    {        // 获取待处理的任务(限制每次处理10条)        $tasks = Db::name('icbc_queue')            ->where('status', 0)            ->where('retry_count', '<', 3) // 最多重试3次            ->limit(10)            ->select();                foreach ($tasks as $task) {            try {                // 更新状态为处理中                Db::name('icbc_queue')->where('id', $task['id'])->update([                    'status' => 1,                    'updatetime' => time()                ]);                                // 调用工行接口                $result = $this->update_villager_integral(                    $task['mobile_phone'],                    $task['integral_value'],                    $task['integral_type'],                    [$task['nickname']]                );                                // 先检查返回值是否为数组                if (!is_array($result)) {                    \think\Log::record('工行接口返回值异常[ID:'.$task['id'].']: ' . var_export($result, true), 'error');                                        // 返回值异常,标记为失败                    $retry_count = $task['retry_count'] + 1;                    $status = $retry_count >= 3 ? 3 : 0;                                        Db::name('icbc_queue')->where('id', $task['id'])->update([                        'status' => $status,                        'retry_count' => $retry_count,                        'result' => json_encode(['error' => '返回值不是数组', 'raw' => var_export($result, true)], JSON_UNESCAPED_UNICODE),                        'error_msg' => '接口返回值格式错误',                        'updatetime' => time()                    ]);                    continue;                }                                // 根据结果更新状态                if (isset($result['return_code']) && $result['return_code'] == 0) {                    // 成功                    Db::name('icbc_queue')->where('id', $task['id'])->update([                        'status' => 2,                        'result' => json_encode($result, JSON_UNESCAPED_UNICODE),                        'updatetime' => time()                    ]);                } else {                    // 失败,增加重试次数                    $retry_count = $task['retry_count'] + 1;                    $status = $retry_count >= 3 ? 3 : 0; // 重试3次后标记为失败                                        $error_msg = isset($result['return_msg']) ? $result['return_msg'] : '未知错误';                                        Db::name('icbc_queue')->where('id', $task['id'])->update([                        'status' => $status,                        'retry_count' => $retry_count,                        'result' => json_encode($result, JSON_UNESCAPED_UNICODE),                        'error_msg' => $error_msg,                        'updatetime' => time()                    ]);                }                            } catch (\Exception $e) {                // 异常处理                Db::name('icbc_queue')->where('id', $task['id'])->update([                    'status' => 0,                    'retry_count' => $task['retry_count'] + 1,                    'error_msg' => $e->getMessage(),                    'updatetime' => time()                ]);                                \think\Log::record('工行队列处理异常[ID:'.$task['id'].']: ' . $e->getMessage(), 'error');            }        }    }    /**     * 写入工行接口日志     * @param bool $is_success 是否成功     * @param array $result 返回结果     * @param array $request_data 请求数据     */    private function writeIcbcLog($is_success, $result, $request_data)    {        // 确定日志文件路径        $log_dir = RUNTIME_PATH . 'icbc_log/';        if (!is_dir($log_dir)) {            mkdir($log_dir, 0755, true);        }                $filename = $is_success ? 'suc.txt' : 'failicbc.txt';        $filepath = $log_dir . $filename;                // 构建日志内容        $log_content = [            '时间' => date('Y-m-d H:i:s'),            '请求数据' => $request_data,            '返回数据' => $result,            '原始响应' => isset($result['raw_response']) ? $result['raw_response'] : '',            '分隔线' => str_repeat('-', 80)        ];                $log_text = "\n" . json_encode($log_content, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n";                // 追加写入文件        file_put_contents($filepath, $log_text, FILE_APPEND);    }    /**     * 清理响应内容,提取纯JSON     * @param string $response 原始响应     * @return string 清理后的JSON字符串     */    private function cleanResponse($response)    {        // 1. 去除首尾空白        $response = trim($response);                // 2. 查找第一个 { 或 [(JSON开始)        $json_start = -1;        $first_brace = strpos($response, '{');        $first_bracket = strpos($response, '[');                if ($first_brace !== false && $first_bracket !== false) {            $json_start = min($first_brace, $first_bracket);        } elseif ($first_brace !== false) {            $json_start = $first_brace;        } elseif ($first_bracket !== false) {            $json_start = $first_bracket;        }                // 3. 查找最后一个 } 或 ](JSON结束)        $json_end = -1;        $last_brace = strrpos($response, '}');        $last_bracket = strrpos($response, ']');                if ($last_brace !== false && $last_bracket !== false) {            $json_end = max($last_brace, $last_bracket);        } elseif ($last_brace !== false) {            $json_end = $last_brace;        } elseif ($last_bracket !== false) {            $json_end = $last_bracket;        }                // 4. 提取JSON部分        if ($json_start !== -1 && $json_end !== -1 && $json_end > $json_start) {            return substr($response, $json_start, $json_end - $json_start + 1);        }                // 5. 如果没找到JSON结构,返回原始内容        return $response;    }    /*     * 查看错题     * Robin     * @param $ids     * */    public function error_ids($ids)    {        $questions = QuestionModel::whereIn('id', ($ids))->select();        $this->success('', $questions);    }}
 |