Paper.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. <?php
  2. namespace addons\exam\controller;
  3. use addons\exam\enum\CommonStatus;
  4. use addons\exam\enum\ExamMode;
  5. use addons\exam\library\ExamService;
  6. use addons\exam\model\PaperModel;
  7. use addons\exam\model\QuestionModel;
  8. use addons\exam\model\UserModel;
  9. use app\admin\model\exam\CateModel;
  10. use app\admin\model\exam\GradeModel;
  11. use think\Request;
  12. use think\Db;
  13. include_once EXTEND_PATH.'icbc/DefaultIcbcClient.php';
  14. /**
  15. * 试卷接口
  16. */
  17. class Paper extends Base
  18. {
  19. protected $noNeedLogin = ['index','lists','detail'];
  20. protected $noNeedRight = ['*'];
  21. protected $user;
  22. /**
  23. * 查询出分类下的试卷
  24. */
  25. public function index()
  26. {
  27. $cate_id = input('cate_id/d', '0');
  28. $sort = input('sort/s', '');
  29. $now = time();
  30. $query = PaperModel::with([
  31. 'cates' => function ($query) {
  32. $query->withField('id, name');
  33. }
  34. ])
  35. ->where('status', CommonStatus::NORMAL)
  36. ->where('is_only_room', 0)// 过滤仅考场使用的试卷
  37. ->whereRaw("((start_time = 0 and end_time = 0) or (start_time < {$now} and end_time > {$now}))");
  38. // 分类
  39. if ($cate_id) {
  40. $child_cate_ids = CateModel::getChildId($cate_id);
  41. array_push($child_cate_ids, $cate_id);
  42. $query->whereIn('cate_id', $child_cate_ids);
  43. }
  44. // 排序
  45. if ($sort && $sort != 'null') {
  46. $sort = explode('|', $sort);
  47. $field = $sort[0];
  48. $order_by = $sort[1];
  49. $field = in_array($field, ['join_count']) ? $field : 'join_count';
  50. $order_by = $order_by == 'desc' ? 'desc' : 'asc';
  51. $query->order("{$field} $order_by");
  52. }
  53. $list = $query->paginate();
  54. $this->success('', ['list' => $list]);
  55. }
  56. public function lists()
  57. {
  58. $list = Db::name('exam_paper')->field('id,image,title,updatetime')->where('status','normal')
  59. ->order('weigh desc, id desc')->autopage()->select();
  60. $list = list_domain_image($list,['image']);
  61. $this->success('', $list);
  62. }
  63. public function detail(){
  64. $id = input('id');
  65. $info = Db::name('exam_paper')->field('id,image,title,content,updatetime,quantity')->where('status','normal')
  66. ->find($id);
  67. $info = info_domain_image($info,['image']);
  68. $this->success('', $info);
  69. }
  70. /**
  71. * 试卷取题接口
  72. */
  73. public function getExamQuestion()
  74. {
  75. $paper_id = input('paper_id/d', 0);
  76. $room_id = input('room_id/d', 0);
  77. // 验证是否需要绑定手机号
  78. // UserModel::isMustBindMobile($this->auth->getUser());
  79. // 预创建考场考试记录
  80. // $room_grade_id = ExamService::preRoomGrade($room_id, $this->auth->id);
  81. // 获取试卷题目
  82. $question_data = ExamService::getExamQuestion($paper_id, $room_id);
  83. // $this->success('', array_merge($question_data, ['room_grade_id' => $room_grade_id]));
  84. $this->success('', $question_data);
  85. }
  86. //开始考试接口
  87. public function startpaper(){
  88. $paper_id = input('paper_id/d', 0);
  89. $user_id = $this->auth->id;
  90. //检查考试状态
  91. /*$check = Db::name('exam_grade')->where('user_id', $user_id)->where('status',1)->find();
  92. if($check){
  93. $this->success('您有其他考试正在进行中,即将继续考试',0);//直接给成功,数据返回0,前端跳转
  94. }*/
  95. //检查试卷
  96. $paper = PaperModel::get($paper_id);
  97. switch (true) {
  98. case !$paper:
  99. $this->error('科普答题信息不存在');
  100. case $paper->status != 'NORMAL':
  101. $this->error('科普答题信息不存在');
  102. case $paper->mode == 'RANDOM' && !$paper->configs:
  103. $this->error('科普答题信息未配置');
  104. }
  105. //时间限制
  106. /*if ($paper['start_time'] > 0 && $paper['start_time'] > time()) {
  107. $this->error('该试卷未开始,不能参与考试');
  108. }
  109. if ($paper['end_time'] > 0 && $paper['end_time'] < time()) {
  110. $this->error('该试卷已结束,不能参与考试');
  111. }*/
  112. //考试资格
  113. /*if(!in_array($user_id,explode(',',$paper['user_ids']))){
  114. $this->error('您不能参加该考试');
  115. }*/
  116. //次数限制
  117. if ($paper['limit_count'] > 0){
  118. $my_count = Db::name('exam_grade')->where('user_id', $user_id)->where('paper_id', $paper_id)->where('status',2)->count();
  119. if($my_count >= $paper['limit_count']) {
  120. $this->error('在该科普中您的答题次数已达上限');
  121. }
  122. }
  123. //记录为已开始,计划任务倒计时之后 自动结束
  124. $data = [
  125. 'cate_id' => $paper['cate_id'],
  126. 'user_id' => $this->auth->id,
  127. 'paper_id' => $paper_id,
  128. 'mode' => $paper['mode'],
  129. 'total_score' => $paper['total_score'],
  130. 'total_count' => $paper['quantity'],
  131. 'start_time' => time(),
  132. 'createtime' => time(),
  133. 'status' => 1,
  134. 'limit_time' => $paper['limit_time'], //限时N秒
  135. 'last_time' => $paper['limit_time'] > 0 ? (time() + $paper['limit_time']) : 0, //最后限制交卷时间,时间戳
  136. ];
  137. $grade_id = Db::name('exam_grade')->insertGetId($data);
  138. $this->success('',$grade_id);
  139. }
  140. //进行中考试
  141. public function half_examing(){
  142. $user_id = $this->auth->id;
  143. $check = Db::name('exam_grade')->where('user_id', $user_id)->where('status',1)->find();
  144. if(empty($check)){
  145. $this->error('您没有进行中的考试');
  146. }
  147. $paper_id = $check['paper_id'];
  148. // 获取试卷题目
  149. $question_data = ExamService::getExamQuestion($paper_id, 0);
  150. $question_data['paper']['limit_time'] = $check['last_time'] - time();//倒计时秒数
  151. $this->success('', $question_data);
  152. }
  153. /**
  154. * 交卷
  155. */
  156. public function submit()
  157. {
  158. $request = Request::instance();
  159. $user_id = $this->auth->id;
  160. $grade_id = $request->post('grade_id');
  161. $paper_id = $request->post('paper_id/d', 0);
  162. $questions = $request->post('questions/a', []);
  163. $start_time = $request->post('start_time/d', time());
  164. $room_id = 0;
  165. $room_grade_id = $request->post('room_grade_id/d', 0);
  166. if (!$user_id || !$paper_id || !$questions) {
  167. $this->error('提交数据有误');
  168. }
  169. $check = Db::name('exam_grade')->where('status',1)->where('id',$grade_id)->where('user_id',$user_id)->where('paper_id',$paper_id)->find();
  170. if(!$check){
  171. $this->error('交卷有误,或者您已交卷');
  172. }
  173. $start_time = $check['start_time'];
  174. // 考场考试
  175. if ($room_id) {
  176. if (!$room_grade_id) {
  177. $this->error('提交数据不合法');
  178. }
  179. // 考场考试
  180. $result = ExamService::roomExam($user_id, $room_id, $room_grade_id, $questions, $start_time, $paper, $room, $is_makeup, $room_grade_log);
  181. // 记录考场考试成绩
  182. $room_grade_log->allowField(true)->save(
  183. array_merge(
  184. $result,
  185. [
  186. // 'cate_id' => $paper['cate_id'],
  187. 'user_id' => $user_id,
  188. 'paper_id' => $paper_id,
  189. 'is_makeup' => $is_makeup,
  190. 'is_pre' => 0, // 提交成绩后不再为预创建标记
  191. ],
  192. [
  193. 'exam_mode' => ExamMode::ROOM,
  194. ]
  195. )
  196. );
  197. } else {
  198. $result = ExamService::paperExam($user_id, $paper_id, $questions, $start_time, $paper);
  199. // 记录考试成绩
  200. /*GradeModel::create(array_merge(
  201. $result,
  202. [
  203. 'cate_id' => $paper['cate_id'],
  204. 'user_id' => $user_id,
  205. 'paper_id' => $paper_id,
  206. ],
  207. [
  208. // 'exam_mode' => ExamMode::PAPER,
  209. 'date' => date('Y-m-d'),
  210. ]), true);*/
  211. $update = array_merge(
  212. $result,
  213. [
  214. 'updatetime' => time(),
  215. 'date' => date('Y-m-d'),
  216. 'status' => 2,
  217. 'finish_time' => time(),
  218. ]);
  219. unset($update['pass_score']);
  220. unset($update['start_time']);
  221. $rs = Db::name('exam_grade')->where('id',$grade_id)->update($update);
  222. if($rs === false){
  223. $this->error('交卷失败');
  224. }
  225. // 异步推送积分到工行(写入队列)
  226. $cephone = '15388010006';
  227. //$cephone = $this->auth->mobile;
  228. $this->addIcbcQueue($cephone, $result['score'], '10938', $this->auth->nickname);
  229. }
  230. $result['nickname'] = $this->auth->nickname;
  231. unset($result['question_ids']);
  232. unset($result['user_answers']);
  233. unset($result['configs']);
  234. $this->success('',$result);
  235. }
  236. /**
  237. * 积分维护接口 - 推送积分数据到工行
  238. * @param string $mobile_phone 用户手机号
  239. * @param int $integral_value 变动的积分值(整数)
  240. * @param string $integral_type 积分类型
  241. * @param array $remarks 备用字段数组(可选,最多7个)
  242. * @return array 返回接口响应结果
  243. */
  244. public function update_villager_integral($mobile_phone = '', $integral_value = 0, $integral_type = '', $remarks = [])
  245. {
  246. // PHP 8+ 兼容:确保参数不为null
  247. $mobile_phone = $mobile_phone ?? '';
  248. $integral_value = $integral_value ?? 0;
  249. $integral_type = $integral_type ?? '';
  250. $remarks = $remarks ?? [];
  251. // 如果没有传入手机号,尝试从登录用户获取
  252. if (empty($mobile_phone) && isset($this->auth->mobile)) {
  253. $mobile_phone = $this->auth->mobile;
  254. }
  255. // 生成16位唯一序列号(时间戳10位+随机数6位)
  256. $fSeqNo = time() . str_pad(mt_rand(0, 999999), 6, '0', STR_PAD_LEFT);
  257. // 构建业务参数
  258. $biz_content = [
  259. 'fSeqNo' => $fSeqNo,
  260. 'corpCode' => 'xingfulishequ',
  261. 'mobilePhone' => $mobile_phone,
  262. 'integralValue' => (string)$integral_value, // 确保是字符串类型
  263. 'integralType' => $integral_type,
  264. ];
  265. // 添加备用字段(如果有)
  266. for ($i = 1; $i <= 7; $i++) {
  267. if (isset($remarks[$i - 1]) && !empty($remarks[$i - 1])) {
  268. $biz_content['remark' . $i] = $remarks[$i - 1];
  269. }
  270. }
  271. // 工行RSA密钥(字符串格式)
  272. $public_key = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwFgHD4kzEVPdOj03ctKM7KV+16bWZ5BMNgvEeuEQwfQYkRVwI9HFOGkwNTMn5hiJXHnlXYCX+zp5r6R52MY0O7BsTCLT7aHaxsANsvI9ABGx3OaTVlPB59M6GPbJh0uXvio0m1r/lTW3Z60RU6Q3oid/rNhP3CiNgg0W6O3AGqwIDAQAB';
  273. $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=';
  274. // 构建通用请求参数
  275. $data = [
  276. 'app_id' => '10000000000004096993',
  277. 'msg_id' => createUniqueNo('msg', time()),
  278. 'format' => 'json',
  279. 'charset' => 'UTF-8',
  280. 'encrypt_type' => 'AES',
  281. 'sign_type' => 'RSA2',
  282. ];
  283. // 记录请求日志(调试用)
  284. \think\Log::record('积分维护接口请求参数: ' . json_encode([
  285. 'app_id' => $data['app_id'],
  286. 'msg_id' => $data['msg_id'],
  287. 'biz_content' => $biz_content
  288. ], JSON_UNESCAPED_UNICODE), 'info');
  289. try {
  290. // 创建工行客户端
  291. $client = new \DefaultIcbcClient(
  292. $data['app_id'],
  293. $private_key,
  294. $data['sign_type'],
  295. $data['charset'],
  296. $data['format'],
  297. $public_key,
  298. '', '', '', ''
  299. );
  300. // 构建请求参数
  301. $request = [
  302. 'serviceUrl' => 'https://gw.dccnet.com.cn:8084/api/mybank/farm/farmplatf/updateVillagerIntegral/V1',
  303. 'method' => 'POST',
  304. 'isNeedEncrypt' => false,
  305. 'extraParams' => null,
  306. 'biz_content' => $biz_content,
  307. ];
  308. // 发送请求
  309. $response = $client->execute($request, $data['msg_id'], '');
  310. // 记录原始响应(调试用)
  311. \think\Log::record('积分维护接口原始响应: ' . $response, 'info');
  312. // 检查响应是否为空
  313. if (empty($response)) {
  314. $resultData = [
  315. 'return_code' => '-1',
  316. 'return_msg' => '接口无响应',
  317. 'raw_response' => $response
  318. ];
  319. $this->writeIcbcLog(false, $resultData, $biz_content);
  320. return $resultData;
  321. }
  322. // 解析响应
  323. $result = json_decode($response, true);
  324. // 检查JSON解析是否成功
  325. if (json_last_error() !== JSON_ERROR_NONE) {
  326. $resultData = [
  327. 'return_code' => '-2',
  328. 'return_msg' => 'JSON解析失败: ' . json_last_error_msg(),
  329. 'raw_response' => $response
  330. ];
  331. $this->writeIcbcLog(false, $resultData, $biz_content);
  332. return $resultData;
  333. }
  334. // 检查是否有response_biz_content(按API文档,响应数据在这个字段里)
  335. if (isset($result['response_biz_content'])) {
  336. $biz_result = json_decode($result['response_biz_content'], true);
  337. if ($biz_result) {
  338. // 合并业务响应和原始响应
  339. $resultData = array_merge($biz_result, ['raw_response' => $response]);
  340. $this->writeIcbcLog($resultData['return_code'] == 0, $resultData, $biz_content);
  341. return $resultData;
  342. }
  343. }
  344. // 如果直接有return_code,直接返回
  345. if (isset($result['return_code'])) {
  346. $this->writeIcbcLog($result['return_code'] == 0, $result, $biz_content);
  347. return $result;
  348. }
  349. // 返回完整结果
  350. $resultData = $result ? $result : [
  351. 'return_code' => '-3',
  352. 'return_msg' => '未知响应格式',
  353. 'raw_response' => $response
  354. ];
  355. $this->writeIcbcLog(false, $resultData, $biz_content);
  356. return $resultData;
  357. } catch (\Exception $e) {
  358. // 捕获异常
  359. \think\Log::record('积分维护接口异常: ' . $e->getMessage(), 'error');
  360. $resultData = [
  361. 'return_code' => '-99',
  362. 'return_msg' => '接口调用异常: ' . $e->getMessage(),
  363. 'raw_response' => ''
  364. ];
  365. $this->writeIcbcLog(false, $resultData, $biz_content);
  366. return $resultData;
  367. }
  368. }
  369. /**
  370. * 添加工行积分任务到队列
  371. * @param string $mobile_phone 手机号
  372. * @param int $integral_value 积分值
  373. * @param string $integral_type 积分类型
  374. * @param string $nickname 用户昵称
  375. */
  376. private function addIcbcQueue($mobile_phone, $integral_value, $integral_type, $nickname)
  377. {
  378. try {
  379. // PHP 8+ 兼容:确保所有值都不为null
  380. $data = [
  381. 'mobile_phone' => $mobile_phone ?? '',
  382. 'integral_value' => $integral_value ?? 0,
  383. 'integral_type' => $integral_type ?? '',
  384. 'nickname' => $nickname ?? '',
  385. 'status' => 0, // 0-待处理, 1-处理中, 2-成功, 3-失败
  386. 'retry_count' => 0,
  387. 'createtime' => time(),
  388. 'updatetime' => time(),
  389. ];
  390. Db::name('icbc_queue')->insert($data);
  391. // 不立即处理,完全由定时任务处理
  392. // 这样可以避免 fastcgi_finish_request() 影响正常响应
  393. } catch (\Exception $e) {
  394. // 队列写入失败,记录日志但不影响主流程
  395. \think\Log::record('工行队列写入失败: ' . $e->getMessage(), 'error');
  396. }
  397. }
  398. /**
  399. * 异步处理工行队列(不阻塞当前请求)
  400. */
  401. private function processIcbcQueueAsync()
  402. {
  403. // 使用fastcgi_finish_request()立即返回响应给用户
  404. if (function_exists('fastcgi_finish_request')) {
  405. fastcgi_finish_request();
  406. }
  407. // 后台处理队列
  408. $this->processIcbcQueue();
  409. }
  410. /**
  411. * 处理工行队列任务
  412. */
  413. public function processIcbcQueue()
  414. {
  415. // 获取待处理的任务(限制每次处理10条)
  416. $tasks = Db::name('icbc_queue')
  417. ->where('status', 0)
  418. ->where('retry_count', '<', 3) // 最多重试3次
  419. ->limit(10)
  420. ->select();
  421. foreach ($tasks as $task) {
  422. try {
  423. // 更新状态为处理中
  424. Db::name('icbc_queue')->where('id', $task['id'])->update([
  425. 'status' => 1,
  426. 'updatetime' => time()
  427. ]);
  428. // 调用工行接口
  429. $result = $this->update_villager_integral(
  430. $task['mobile_phone'],
  431. $task['integral_value'],
  432. $task['integral_type'],
  433. [$task['nickname']]
  434. );
  435. // 根据结果更新状态
  436. if (isset($result['return_code']) && $result['return_code'] == 0) {
  437. // 成功
  438. Db::name('icbc_queue')->where('id', $task['id'])->update([
  439. 'status' => 2,
  440. 'result' => json_encode($result, JSON_UNESCAPED_UNICODE),
  441. 'updatetime' => time()
  442. ]);
  443. } else {
  444. // 失败,增加重试次数
  445. $retry_count = $task['retry_count'] + 1;
  446. $status = $retry_count >= 3 ? 3 : 0; // 重试3次后标记为失败
  447. Db::name('icbc_queue')->where('id', $task['id'])->update([
  448. 'status' => $status,
  449. 'retry_count' => $retry_count,
  450. 'result' => json_encode($result, JSON_UNESCAPED_UNICODE),
  451. 'error_msg' => isset($result['return_msg']) ? $result['return_msg'] : '未知错误',
  452. 'updatetime' => time()
  453. ]);
  454. }
  455. } catch (\Exception $e) {
  456. // 异常处理
  457. Db::name('icbc_queue')->where('id', $task['id'])->update([
  458. 'status' => 0,
  459. 'retry_count' => $task['retry_count'] + 1,
  460. 'error_msg' => $e->getMessage(),
  461. 'updatetime' => time()
  462. ]);
  463. \think\Log::record('工行队列处理异常[ID:'.$task['id'].']: ' . $e->getMessage(), 'error');
  464. }
  465. }
  466. }
  467. /**
  468. * 写入工行接口日志
  469. * @param bool $is_success 是否成功
  470. * @param array $result 返回结果
  471. * @param array $request_data 请求数据
  472. */
  473. private function writeIcbcLog($is_success, $result, $request_data)
  474. {
  475. // 确定日志文件路径
  476. $log_dir = RUNTIME_PATH . 'icbc_log/';
  477. if (!is_dir($log_dir)) {
  478. mkdir($log_dir, 0755, true);
  479. }
  480. $filename = $is_success ? 'suc.txt' : 'failicbc.txt';
  481. $filepath = $log_dir . $filename;
  482. // 构建日志内容
  483. $log_content = [
  484. '时间' => date('Y-m-d H:i:s'),
  485. '请求数据' => $request_data,
  486. '返回数据' => $result,
  487. '原始响应' => isset($result['raw_response']) ? $result['raw_response'] : '',
  488. '分隔线' => str_repeat('-', 80)
  489. ];
  490. $log_text = "\n" . json_encode($log_content, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n";
  491. // 追加写入文件
  492. file_put_contents($filepath, $log_text, FILE_APPEND);
  493. }
  494. /*
  495. * 查看错题
  496. * Robin
  497. * @param $ids
  498. * */
  499. public function error_ids($ids)
  500. {
  501. $questions = QuestionModel::whereIn('id', ($ids))->select();
  502. $this->success('', $questions);
  503. }
  504. }