707 lines
24 KiB
PHP
707 lines
24 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace app\common\service\wechat;
|
||
|
||
use think\facade\Cache;
|
||
use think\facade\Log;
|
||
|
||
/**
|
||
* 企业微信服务类
|
||
* @see https://developer.work.weixin.qq.com/document/
|
||
*/
|
||
class WechatWorkService
|
||
{
|
||
private $corpId;
|
||
private $secret;
|
||
private $contactSecret;
|
||
private $accessToken;
|
||
private $contactAccessToken;
|
||
|
||
public function __construct()
|
||
{
|
||
$this->corpId = config('pay.wechat_work.corp_id');
|
||
$this->secret = config('pay.wechat_work.external_pay_secret');
|
||
$this->contactSecret = config('pay.wechat_work.contact_secret');
|
||
|
||
Log::info('企业微信配置: ' . json_encode([
|
||
'corp_id' => $this->corpId,
|
||
'secret' => substr($this->secret, 0, 10) . '...',
|
||
'contact_secret' => substr($this->contactSecret, 0, 10) . '...'
|
||
]));
|
||
}
|
||
|
||
/**
|
||
* @notes 获取access_token
|
||
* @see https://developer.work.weixin.qq.com/document/path/91039
|
||
*/
|
||
private function getAccessToken(): string
|
||
{
|
||
if ($this->accessToken) {
|
||
return $this->accessToken;
|
||
}
|
||
|
||
// 从缓存获取
|
||
$cacheKey = 'qywx_access_token';
|
||
$token = Cache::get($cacheKey);
|
||
if ($token) {
|
||
$this->accessToken = $token;
|
||
return $token;
|
||
}
|
||
|
||
// 请求新token
|
||
$url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken';
|
||
$params = [
|
||
'corpid' => $this->corpId,
|
||
'corpsecret' => $this->secret,
|
||
];
|
||
|
||
$response = $this->httpGet($url, $params);
|
||
if (isset($response['access_token'])) {
|
||
$token = $response['access_token'];
|
||
$expiresIn = $response['expires_in'] ?? 7200;
|
||
|
||
// 缓存token,提前5分钟过期
|
||
Cache::set($cacheKey, $token, $expiresIn - 300);
|
||
|
||
$this->accessToken = $token;
|
||
return $token;
|
||
}
|
||
|
||
throw new \Exception('获取access_token失败: ' . ($response['errmsg'] ?? '未知错误'));
|
||
}
|
||
|
||
/**
|
||
* @notes 获取客户联系的access_token
|
||
* @see https://developer.work.weixin.qq.com/document/path/92113
|
||
*/
|
||
private function getContactAccessToken(): string
|
||
{
|
||
if ($this->contactAccessToken) {
|
||
return $this->contactAccessToken;
|
||
}
|
||
|
||
// 从缓存获取
|
||
$cacheKey = 'qywx_contact_access_token';
|
||
$token = Cache::get($cacheKey);
|
||
if ($token) {
|
||
$this->contactAccessToken = $token;
|
||
return $token;
|
||
}
|
||
|
||
// 尝试使用 contactSecret 获取 token
|
||
$token = $this->requestToken($this->contactSecret, $cacheKey, '客户联系');
|
||
if ($token) {
|
||
$this->contactAccessToken = $token;
|
||
return $token;
|
||
}
|
||
|
||
// 如果 contactSecret 失败,尝试使用 secret
|
||
Log::info('企业微信-使用 contactSecret 获取 token 失败,尝试使用 secret');
|
||
$token = $this->requestToken($this->secret, $cacheKey, '普通应用');
|
||
if ($token) {
|
||
$this->contactAccessToken = $token;
|
||
return $token;
|
||
}
|
||
|
||
throw new \Exception('获取客户联系access_token失败');
|
||
}
|
||
|
||
/**
|
||
* @notes 请求获取 token
|
||
*/
|
||
private function requestToken(string $secret, string $cacheKey, string $type): string
|
||
{
|
||
$url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken';
|
||
$params = [
|
||
'corpid' => $this->corpId,
|
||
'corpsecret' => $secret,
|
||
];
|
||
|
||
Log::info('企业微信-获取' . $type . 'AccessToken请求: ' . json_encode($params, JSON_UNESCAPED_UNICODE));
|
||
|
||
$response = $this->httpGet($url, $params);
|
||
|
||
Log::info('企业微信-获取' . $type . 'AccessToken响应: ' . json_encode($response, JSON_UNESCAPED_UNICODE));
|
||
|
||
if (isset($response['access_token'])) {
|
||
$token = $response['access_token'];
|
||
$expiresIn = $response['expires_in'] ?? 7200;
|
||
|
||
// 缓存token,提前5分钟过期
|
||
Cache::set($cacheKey, $token, $expiresIn - 300);
|
||
|
||
return $token;
|
||
}
|
||
|
||
Log::error('获取' . $type . 'token失败: ' . json_encode($response));
|
||
return '';
|
||
}
|
||
|
||
/**
|
||
* @notes 获取部门成员列表
|
||
* @see https://developer.work.weixin.qq.com/document/path/90200
|
||
*/
|
||
public function getDepartmentUserList(int $departmentId = 1, bool $fetchChild = true): array
|
||
{
|
||
// 获取部门成员列表使用 external_pay_secret(普通应用Secret)
|
||
$accessToken = $this->getAccessToken();
|
||
$url = 'https://qyapi.weixin.qq.com/cgi-bin/user/list';
|
||
$params = [
|
||
'access_token' => $accessToken,
|
||
'department_id' => $departmentId,
|
||
'fetch_child' => $fetchChild ? 1 : 0,
|
||
];
|
||
|
||
Log::info('企业微信-获取部门成员列表请求: ' . json_encode($params, JSON_UNESCAPED_UNICODE));
|
||
|
||
$response = $this->httpGet($url, $params);
|
||
|
||
Log::info('企业微信-获取部门成员列表响应: ' . json_encode($response, JSON_UNESCAPED_UNICODE));
|
||
|
||
if (isset($response['userlist'])) {
|
||
return $response['userlist'];
|
||
}
|
||
|
||
Log::error('获取部门成员列表失败: ' . json_encode($response));
|
||
return [];
|
||
}
|
||
|
||
/**
|
||
* @notes 获取成员的客户列表
|
||
* @see https://developer.work.weixin.qq.com/document/path/92113
|
||
* @param string $userId 成员ID
|
||
* @param string $cursor 分页游标,上次返回的 next_cursor
|
||
* @param int $limit 最大记录数,最大 100,默认 50
|
||
* @return array|false 返回客户列表数组,权限错误返回false
|
||
*/
|
||
public function getExternalContactList(string $userId, string $cursor = '', int $limit = 50)
|
||
{
|
||
// 生成缓存键
|
||
$cacheKey = 'qywx_external_contact_list_' . $userId . '_' . $cursor . '_' . $limit;
|
||
|
||
// 尝试从缓存获取
|
||
$cachedResult = Cache::get($cacheKey);
|
||
if ($cachedResult) {
|
||
Log::info('从缓存获取客户列表: ' . $userId);
|
||
return $cachedResult;
|
||
}
|
||
|
||
$accessToken = $this->getContactAccessToken();
|
||
$url = 'https://qyapi.weixin.qq.com/cgi-bin/externalcontact/list';
|
||
$params = [
|
||
'access_token' => $accessToken,
|
||
'userid' => $userId,
|
||
];
|
||
|
||
// 添加分页参数
|
||
if (!empty($cursor)) {
|
||
$params['cursor'] = $cursor;
|
||
}
|
||
if ($limit > 0) {
|
||
$params['limit'] = min($limit, 100); // 最大100
|
||
}
|
||
|
||
$response = $this->httpGet($url, $params);
|
||
|
||
Log::info('企业微信-获取成员客户列表响应 userId=' . $userId . ': ' . json_encode($response, JSON_UNESCAPED_UNICODE));
|
||
|
||
// 检查是否是权限错误
|
||
if (isset($response['errcode']) && $response['errcode'] == 48002) {
|
||
Log::error('API权限不足(48002): 请在企业微信管理后台开启"客户联系"权限');
|
||
return false;
|
||
}
|
||
|
||
if (isset($response['external_userid'])) {
|
||
$result = [
|
||
'external_userid' => $response['external_userid'],
|
||
'next_cursor' => $response['next_cursor'] ?? ''
|
||
];
|
||
|
||
// 缓存结果,有效期5分钟
|
||
Cache::set($cacheKey, $result, 300);
|
||
return $result;
|
||
}
|
||
|
||
// 如果没有客户,返回空数组(不记录错误)
|
||
if (isset($response['errcode']) && $response['errcode'] == 0) {
|
||
$result = [
|
||
'external_userid' => [],
|
||
'next_cursor' => ''
|
||
];
|
||
|
||
// 缓存结果,有效期5分钟
|
||
Cache::set($cacheKey, $result, 300);
|
||
return $result;
|
||
}
|
||
|
||
Log::error('获取客户列表失败: ' . json_encode($response));
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* @notes 获取外部联系人信息
|
||
* @see https://developer.work.weixin.qq.com/document/path/92114
|
||
* @param string $externalUserId 外部用户ID
|
||
* @return array|false 返回外部联系人信息,失败返回false
|
||
*/
|
||
public function getExternalContactInfo(string $externalUserId)
|
||
{
|
||
$accessToken = $this->getAccessToken();
|
||
$url = 'https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get';
|
||
$params = [
|
||
'access_token' => $accessToken,
|
||
'external_userid' => $externalUserId,
|
||
];
|
||
|
||
$response = $this->httpGet($url, $params);
|
||
|
||
Log::info('企业微信-获取外部联系人信息响应: ' . json_encode($response, JSON_UNESCAPED_UNICODE));
|
||
|
||
if (isset($response['external_contact'])) {
|
||
return $response['external_contact'];
|
||
}
|
||
|
||
Log::error('获取外部联系人信息失败: ' . json_encode($response));
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* @notes 生成带参数的二维码
|
||
* @see https://developer.work.weixin.qq.com/document/path/90239
|
||
* @param string $scene 场景值
|
||
* @param int $expireSeconds 过期时间(秒),最大2592000
|
||
* @return array|false 返回二维码信息,失败返回false
|
||
*/
|
||
public function createQrCode(string $scene, int $expireSeconds = 2592000)
|
||
{
|
||
$accessToken = $this->getAccessToken();
|
||
$url = "https://qyapi.weixin.qq.com/cgi-bin/qrcode/create?access_token={$accessToken}";
|
||
|
||
$data = [
|
||
"action_info" => [
|
||
"scene" => [
|
||
"scene_str" => $scene
|
||
]
|
||
],
|
||
"expire_seconds" => $expireSeconds,
|
||
"action_name" => "QR_SCENE"
|
||
];
|
||
|
||
Log::info('企业微信-生成二维码请求: ' . json_encode($data, JSON_UNESCAPED_UNICODE));
|
||
|
||
$response = $this->httpPost($url, json_encode($data));
|
||
|
||
Log::info('企业微信-生成二维码响应: ' . json_encode($response, JSON_UNESCAPED_UNICODE));
|
||
|
||
if (isset($response['ticket'])) {
|
||
return $response;
|
||
}
|
||
|
||
Log::error('生成二维码失败: ' . json_encode($response));
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* @notes 获取客户详情
|
||
* @see https://developer.work.weixin.qq.com/document/path/92114
|
||
*/
|
||
public function getExternalContactDetail(string $externalUserId): array
|
||
{
|
||
$accessToken = $this->getContactAccessToken();
|
||
$url = 'https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get';
|
||
$params = [
|
||
'access_token' => $accessToken,
|
||
'external_userid' => $externalUserId,
|
||
];
|
||
|
||
$response = $this->httpGet($url, $params);
|
||
|
||
if (isset($response['external_contact'])) {
|
||
return $response;
|
||
}
|
||
|
||
Log::error('获取客户详情失败: ' . json_encode($response));
|
||
return [];
|
||
}
|
||
|
||
/**
|
||
* @notes 获取所有员工的客户列表
|
||
* @return array 所有员工的客户ID列表,格式:[staff_id => [external_userid1, external_userid2]]
|
||
*/
|
||
public function getAllStaffCustomers(): array
|
||
{
|
||
$result = [];
|
||
|
||
// 获取所有员工
|
||
$staffList = $this->getDepartmentUserList();
|
||
|
||
if (empty($staffList)) {
|
||
Log::info('企业微信-未获取到员工列表');
|
||
return $result;
|
||
}
|
||
|
||
Log::info('企业微信-开始获取员工客户列表,共 ' . count($staffList) . ' 名员工');
|
||
|
||
foreach ($staffList as $staff) {
|
||
$userId = $staff['userid'] ?? '';
|
||
if (empty($userId)) {
|
||
continue;
|
||
}
|
||
|
||
$customerResult = $this->getExternalContactList($userId);
|
||
|
||
if ($customerResult === false) {
|
||
Log::error('企业微信-员工 ' . $userId . ' 获取客户列表权限不足');
|
||
continue;
|
||
}
|
||
|
||
$customerIds = $customerResult['external_userid'] ?? [];
|
||
|
||
if (!empty($customerIds)) {
|
||
$result[$userId] = [
|
||
'name' => $staff['name'] ?? '',
|
||
'customers' => $customerIds
|
||
];
|
||
Log::info('企业微信-员工 ' . $userId . '(' . ($staff['name'] ?? '') . ') 有 ' . count($customerIds) . ' 个客户');
|
||
}
|
||
}
|
||
|
||
Log::info('企业微信-共获取到 ' . count($result) . ' 名员工的客户');
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* @notes 同步所有客户到本地数据库
|
||
* @return array 同步结果统计
|
||
*/
|
||
public function syncAllExternalContacts(): array
|
||
{
|
||
$stats = [
|
||
'total_staff' => 0,
|
||
'total_customers' => 0,
|
||
'added' => 0,
|
||
'updated' => 0,
|
||
'errors' => 0
|
||
];
|
||
|
||
// 获取所有员工的客户
|
||
$allStaffCustomers = $this->getAllStaffCustomers();
|
||
|
||
if (empty($allStaffCustomers)) {
|
||
Log::info('企业微信-没有客户需要同步');
|
||
return $stats;
|
||
}
|
||
|
||
$stats['total_staff'] = count($allStaffCustomers);
|
||
|
||
foreach ($allStaffCustomers as $staffId => $staffData) {
|
||
$staffName = $staffData['name'] ?? '';
|
||
$customers = $staffData['customers'] ?? [];
|
||
|
||
foreach ($customers as $externalUserId) {
|
||
$stats['total_customers']++;
|
||
|
||
try {
|
||
// 获取客户详情
|
||
$customerDetail = $this->getExternalContactDetail($externalUserId);
|
||
|
||
if (empty($customerDetail) || !isset($customerDetail['external_contact'])) {
|
||
Log::error('企业微信-获取客户详情失败: ' . $externalUserId);
|
||
$stats['errors']++;
|
||
continue;
|
||
}
|
||
|
||
$contactInfo = $customerDetail['external_contact'];
|
||
|
||
// 更新到数据库
|
||
$result = $this->saveExternalContact($staffId, $staffName, $contactInfo);
|
||
|
||
if ($result) {
|
||
$stats[$result]++;
|
||
}
|
||
} catch (\Exception $e) {
|
||
Log::error('企业微信-同步客户异常: ' . $e->getMessage());
|
||
$stats['errors']++;
|
||
}
|
||
}
|
||
}
|
||
|
||
Log::info('企业微信-同步完成: ' . json_encode($stats));
|
||
return $stats;
|
||
}
|
||
|
||
/**
|
||
* @notes 批量获取成员的客户详情(支持分页)
|
||
* @param string $userId 成员ID
|
||
* @param string $cursor 分页游标,上次返回的 next_cursor
|
||
* @param int $limit 最大记录数,最大 100,默认 50
|
||
* @return array|false 返回客户详情列表,权限错误返回false
|
||
*/
|
||
public function batchGetCustomerDetails(string $userId, string $cursor = '', int $limit = 50)
|
||
{
|
||
// 生成缓存键
|
||
$cacheKey = 'qywx_batch_customer_details_' . $userId . '_' . $cursor . '_' . $limit;
|
||
|
||
// 尝试从缓存获取
|
||
$cachedResult = Cache::get($cacheKey);
|
||
if ($cachedResult) {
|
||
Log::info('从缓存获取客户详情: ' . $userId);
|
||
return $cachedResult;
|
||
}
|
||
|
||
// 先获取客户ID列表
|
||
$listResult = $this->getExternalContactList($userId, $cursor, $limit);
|
||
if ($listResult === false) {
|
||
return false;
|
||
}
|
||
|
||
$externalUserIds = $listResult['external_userid'] ?? [];
|
||
$nextCursor = $listResult['next_cursor'] ?? '';
|
||
|
||
$customers = [];
|
||
|
||
// 批量获取客户详情
|
||
foreach ($externalUserIds as $externalUserId) {
|
||
$detail = $this->getExternalContactDetail($externalUserId);
|
||
if (!empty($detail) && isset($detail['external_contact'])) {
|
||
$customers[] = $detail;
|
||
}
|
||
}
|
||
|
||
$result = [
|
||
'customers' => $customers,
|
||
'next_cursor' => $nextCursor,
|
||
'count' => count($customers)
|
||
];
|
||
|
||
// 缓存结果,有效期5分钟
|
||
Cache::set($cacheKey, $result, 300);
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* @notes 保存外部联系人到数据库
|
||
* @param string $staffId 员工ID
|
||
* @param string $staffName 员工姓名
|
||
* @param array $contactInfo 联系人信息
|
||
* @return string|false 返回added/updated,失败返回false
|
||
*/
|
||
private function saveExternalContact(string $staffId, string $staffName, array $contactInfo)
|
||
{
|
||
$externalUserId = $contactInfo['external_userid'] ?? '';
|
||
if (empty($externalUserId)) {
|
||
return false;
|
||
}
|
||
|
||
// 查询是否已存在
|
||
$model = new \app\common\model\QywxExternalContact();
|
||
$exists = $model->where('external_user_id', $externalUserId)->find();
|
||
|
||
$data = [
|
||
'external_user_id' => $externalUserId,
|
||
'name' => $contactInfo['name'] ?? '',
|
||
'avatar' => $contactInfo['avatar'] ?? '',
|
||
'type' => $contactInfo['type'] ?? 1,
|
||
'gender' => $contactInfo['gender'] ?? 0,
|
||
'unionid' => $contactInfo['unionid'] ?? '',
|
||
'position' => $contactInfo['position'] ?? '',
|
||
'corp_name' => $contactInfo['corp_name'] ?? '',
|
||
'corp_full_name' => $contactInfo['corp_full_name'] ?? '',
|
||
'external_profile' => json_encode($contactInfo['external_profile'] ?? [], JSON_UNESCAPED_UNICODE),
|
||
'update_time' => time()
|
||
];
|
||
|
||
// 构建跟进人信息
|
||
$followUser = [
|
||
'userid' => $staffId,
|
||
'name' => $staffName,
|
||
'add_time' => time()
|
||
];
|
||
|
||
if ($exists) {
|
||
// 更新已有记录
|
||
$existsData = json_decode($exists['follow_users'] ?? '{"followers":[]}', true);
|
||
if (!isset($existsData['followers'])) {
|
||
$existsData = ['followers' => []];
|
||
}
|
||
|
||
// 检查是否已存在该跟进人
|
||
$found = false;
|
||
foreach ($existsData['followers'] as &$follower) {
|
||
if ($follower['userid'] === $staffId) {
|
||
$follower = $followUser;
|
||
$found = true;
|
||
break;
|
||
}
|
||
}
|
||
unset($follower);
|
||
|
||
if (!$found) {
|
||
$existsData['followers'][] = $followUser;
|
||
}
|
||
|
||
$data['follow_users'] = json_encode($existsData, JSON_UNESCAPED_UNICODE);
|
||
$data['update_time'] = time();
|
||
|
||
$exists->save($data);
|
||
|
||
return 'updated';
|
||
} else {
|
||
// 新增记录
|
||
$data['follow_users'] = json_encode(['followers' => [$followUser]], JSON_UNESCAPED_UNICODE);
|
||
$data['create_time'] = time();
|
||
|
||
$model->save($data);
|
||
|
||
return 'added';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notes 检查应用权限(用于诊断)
|
||
* @return array 返回应用的权限信息
|
||
*/
|
||
public function checkAppPermissions(): array
|
||
{
|
||
try {
|
||
$accessToken = $this->getAccessToken();
|
||
|
||
// 尝试获取应用详情
|
||
$url = 'https://qyapi.weixin.qq.com/cgi-bin/agent/get';
|
||
$params = [
|
||
'access_token' => $accessToken,
|
||
'agentid' => config('pay.wechat_work.agent_id', 1000002),
|
||
];
|
||
|
||
$response = $this->httpGet($url, $params);
|
||
|
||
return [
|
||
'success' => isset($response['agentid']),
|
||
'data' => $response,
|
||
'message' => isset($response['errmsg']) ? $response['errmsg'] : 'OK',
|
||
];
|
||
} catch (\Throwable $e) {
|
||
return [
|
||
'success' => false,
|
||
'data' => [],
|
||
'message' => $e->getMessage(),
|
||
];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notes 创建外部客户(扫码添加)
|
||
* @param string $staffId 员工ID
|
||
* @param string $staffName 员工姓名
|
||
* @param array $customerInfo 客户信息
|
||
* @return array|false 返回创建结果,失败返回false
|
||
*/
|
||
public function createExternalContact(string $staffId, string $staffName, array $customerInfo)
|
||
{
|
||
try {
|
||
$accessToken = $this->getContactAccessToken();
|
||
$url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/add_contact?access_token={$accessToken}";
|
||
|
||
$data = [
|
||
"userid" => $staffId,
|
||
"external_contact" => [
|
||
"name" => $customerInfo['name'] ?? '未知客户',
|
||
"mobile" => $customerInfo['mobile'] ?? '',
|
||
"position" => $customerInfo['position'] ?? '',
|
||
"corp_name" => $customerInfo['corp_name'] ?? '',
|
||
"avatar" => $customerInfo['avatar'] ?? ''
|
||
]
|
||
];
|
||
|
||
Log::info('企业微信-创建外部客户请求: ' . json_encode($data, JSON_UNESCAPED_UNICODE));
|
||
|
||
$response = $this->httpPost($url, json_encode($data));
|
||
|
||
Log::info('企业微信-创建外部客户响应: ' . json_encode($response, JSON_UNESCAPED_UNICODE));
|
||
|
||
if (isset($response['external_userid'])) {
|
||
return $response;
|
||
}
|
||
|
||
Log::error('创建外部客户失败: ' . json_encode($response));
|
||
return false;
|
||
} catch (\Exception $e) {
|
||
Log::error('创建外部客户异常: ' . $e->getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notes HTTP POST请求
|
||
*/
|
||
private function httpPost(string $url, string $data): array
|
||
{
|
||
$ch = curl_init();
|
||
curl_setopt($ch, CURLOPT_URL, $url);
|
||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||
curl_setopt($ch, CURLOPT_POST, true);
|
||
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||
'Content-Type: application/json',
|
||
'Content-Length: ' . strlen($data)
|
||
]);
|
||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||
|
||
$output = curl_exec($ch);
|
||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||
curl_close($ch);
|
||
|
||
if ($httpCode !== 200) {
|
||
Log::error('HTTP请求失败: ' . $url . ', HTTP Code: ' . $httpCode);
|
||
return [];
|
||
}
|
||
|
||
$result = json_decode($output, true);
|
||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||
Log::error('JSON解析失败: ' . $output);
|
||
return [];
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* @notes HTTP GET请求
|
||
*/
|
||
private function httpGet(string $url, array $params = []): array
|
||
{
|
||
if (!empty($params)) {
|
||
$url .= '?' . http_build_query($params);
|
||
}
|
||
|
||
$ch = curl_init();
|
||
curl_setopt($ch, CURLOPT_URL, $url);
|
||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||
|
||
$response = curl_exec($ch);
|
||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||
curl_close($ch);
|
||
|
||
if ($httpCode !== 200) {
|
||
Log::error('HTTP请求失败: ' . $url . ', HTTP Code: ' . $httpCode);
|
||
return [];
|
||
}
|
||
|
||
$result = json_decode($response, true);
|
||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||
Log::error('JSON解析失败: ' . $response);
|
||
return [];
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
}
|