219 lines
6.5 KiB
PHP
219 lines
6.5 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 $accessToken;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->corpId = config('pay.wechat_work.corp_id');
|
|
$this->secret = config('pay.wechat_work.external_pay_secret');
|
|
}
|
|
|
|
/**
|
|
* @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 获取部门成员列表
|
|
* @see https://developer.work.weixin.qq.com/document/path/90200
|
|
*/
|
|
public function getDepartmentUserList(int $departmentId = 1, bool $fetchChild = true): array
|
|
{
|
|
$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,
|
|
];
|
|
|
|
$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
|
|
* @return array|false 返回客户列表数组,权限错误返回false
|
|
*/
|
|
public function getExternalContactList(string $userId)
|
|
{
|
|
$accessToken = $this->getAccessToken();
|
|
$url = 'https://qyapi.weixin.qq.com/cgi-bin/externalcontact/list';
|
|
$params = [
|
|
'access_token' => $accessToken,
|
|
'userid' => $userId,
|
|
];
|
|
|
|
$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'])) {
|
|
return $response['external_userid'];
|
|
}
|
|
|
|
// 如果没有客户,返回空数组(不记录错误)
|
|
if (isset($response['errcode']) && $response['errcode'] == 0) {
|
|
return [];
|
|
}
|
|
|
|
Log::error('获取客户列表失败: ' . json_encode($response));
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* @notes 获取客户详情
|
|
* @see https://developer.work.weixin.qq.com/document/path/92114
|
|
*/
|
|
public function getExternalContactDetail(string $externalUserId): array
|
|
{
|
|
$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);
|
|
|
|
if (isset($response['external_contact'])) {
|
|
return $response;
|
|
}
|
|
|
|
Log::error('获取客户详情失败: ' . json_encode($response));
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* @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 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;
|
|
}
|
|
}
|