新增
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user