setName('icbc_queue') ->setDescription('处理工行积分推送队列'); } protected function execute(Input $input, Output $output) { $output->writeln("开始处理工行积分队列..."); // 获取待处理的任务 $tasks = Db::name('icbc_queue') ->where('status', 0) ->where('retry_count', '<', 3) ->limit(50) ->select(); if (empty($tasks)) { $output->writeln("没有待处理的任务"); return; } $output->writeln("找到 " . count($tasks) . " 个待处理任务"); foreach ($tasks as $task) { try { // 更新状态为处理中 Db::name('icbc_queue')->where('id', $task['id'])->update([ 'status' => 1, 'updatetime' => time() ]); $output->writeln("处理任务 ID: {$task['id']}, 手机号: {$task['mobile_phone']}, 积分: {$task['integral_value']}"); // 调用工行接口 $result = $this->callIcbcApi($task, $output); // 先检查返回值是否为数组 if (!is_array($result)) { $output->writeln("✗ 任务 {$task['id']} 返回值异常: " . var_export($result, true)); // 返回值异常,标记为失败 $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() ]); $output->writeln("✓ 任务 {$task['id']} 处理成功"); } else { // 失败,增加重试次数 $retry_count = $task['retry_count'] + 1; $status = $retry_count >= 3 ? 3 : 0; $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() ]); $output->writeln("✗ 任务 {$task['id']} 处理失败: " . $error_msg); } } 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() ]); $output->writeln("✗ 任务 {$task['id']} 异常: " . $e->getMessage()); } // 避免请求过快 usleep(100000); // 0.1秒 } $output->writeln("队列处理完成"); } /** * 调用工行接口 */ private function callIcbcApi($task, $output) { include_once EXTEND_PATH . 'icbc/DefaultIcbcClient.php'; // 生成16位唯一序列号 $fSeqNo = time() . str_pad(mt_rand(0, 999999), 6, '0', STR_PAD_LEFT); // 构建业务参数 (PHP 8+ 兼容) $biz_content = [ 'fSeqNo' => $fSeqNo, 'corpCode' => 'xingfulishequ', 'mobilePhone' => $task['mobile_phone'] ?? '', 'integralValue' => (string)($task['integral_value'] ?? 0), 'integralType' => $task['integral_type'] ?? '', 'remark1' => $task['nickname'] ?? '', ]; // 工行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', ]; try { // 创建工行客户端 $client = new \DefaultIcbcClient( $data['app_id'], $private_key, $data['sign_type'], $data['charset'], $data['format'], $public_key, '', '', '', '' ); // 构建请求参数 $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'], ''); // 记录原始响应(用于调试) $output->writeln(" 原始响应长度: " . strlen($response)); // 检查响应 if (empty($response)) { return [ 'return_code' => '-1', 'return_msg' => '接口无响应', 'raw_response' => $response ]; } // 清理响应内容:提取 JSON 部分 // 工行接口可能返回调试信息,需要清理 $clean_response = $this->cleanResponse($response); // 如果清理后的响应与原始不同,记录 if ($clean_response !== $response) { $output->writeln(" ⚠ 响应包含非JSON内容,已清理"); } // 解析响应 $result = json_decode($clean_response, true); if (json_last_error() !== JSON_ERROR_NONE) { $output->writeln(" ✗ JSON解析失败: " . json_last_error_msg()); return [ 'return_code' => '-2', 'return_msg' => 'JSON解析失败: ' . json_last_error_msg(), 'raw_response' => $response, 'clean_response' => $clean_response ]; } // 写入日志 if (isset($result['return_code'])) { $this->writeLog($result['return_code'] == 0, $result, $biz_content); } return $result; } catch (\Exception $e) { return ['return_code' => '-99', 'return_msg' => '异常: ' . $e->getMessage()]; } } /** * 写入日志 */ private function writeLog($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, '分隔线' => 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; } }