You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
5.5 KiB
173 lines
5.5 KiB
<?php
|
|
// +----------------------------------------------------------------------
|
|
// | Niucloud-admin 企业快速开发的多应用管理平台
|
|
// +----------------------------------------------------------------------
|
|
// | 官方网址:https://www.niucloud.com
|
|
// +----------------------------------------------------------------------
|
|
// | niucloud团队 版权所有 开源版本可自由商用
|
|
// +----------------------------------------------------------------------
|
|
// | Author: Niucloud Team
|
|
// +----------------------------------------------------------------------
|
|
|
|
namespace app\service\api\login;
|
|
|
|
use app\model\sys\SysConfig;
|
|
use core\base\BaseService;
|
|
use core\exception\CommonException;
|
|
use think\facade\Cache;
|
|
|
|
/**
|
|
* 微信服务类
|
|
*/
|
|
class WechatService extends BaseService
|
|
{
|
|
/**
|
|
* 获取微信公众号配置
|
|
* @return array
|
|
* @throws \Exception
|
|
*/
|
|
private function getWechatConfig()
|
|
{
|
|
$config = new SysConfig();
|
|
$wechatConfig = $config->where('config_key', 'WECHAT')->find();
|
|
|
|
if (!$wechatConfig || empty($wechatConfig['value'])) {
|
|
throw new CommonException('微信公众号未配置');
|
|
}
|
|
|
|
$configData = $wechatConfig['value'];
|
|
|
|
// 如果是字符串,则解析JSON
|
|
if (is_string($configData)) {
|
|
$configData = json_decode($configData, true);
|
|
}
|
|
|
|
// 如果解析失败或不是数组,抛出异常
|
|
if (!is_array($configData) || empty($configData['app_id']) || empty($configData['app_secret'])) {
|
|
throw new CommonException('微信公众号配置不完整');
|
|
}
|
|
|
|
return $configData;
|
|
}
|
|
|
|
/**
|
|
* 获取微信公众号授权URL
|
|
* @param string $miniOpenid
|
|
* @return array
|
|
* @throws \Exception
|
|
*/
|
|
public function getAuthUrl(string $miniOpenid)
|
|
{
|
|
$config = $this->getWechatConfig();
|
|
|
|
// 生成state参数,用于传递小程序openid
|
|
$state = base64_encode($miniOpenid);
|
|
|
|
// 获取当前域名
|
|
$domain = request()->domain();
|
|
$redirectUri = $domain . '/api/wechat/callback';
|
|
|
|
// 构建微信授权URL
|
|
$authUrl = 'https://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query([
|
|
'appid' => $config['app_id'],
|
|
'redirect_uri' => urlencode($redirectUri),
|
|
'response_type' => 'code',
|
|
'scope' => 'snsapi_userinfo',
|
|
'state' => $state
|
|
]) . '#wechat_redirect';
|
|
|
|
return [
|
|
'auth_url' => $authUrl,
|
|
'redirect_uri' => $redirectUri
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 处理微信授权回调
|
|
* @param string $code
|
|
* @param string $state
|
|
* @return array
|
|
* @throws \Exception
|
|
*/
|
|
public function handleCallback(string $code, string $state)
|
|
{
|
|
$config = $this->getWechatConfig();
|
|
|
|
// 解析state获取小程序openid
|
|
$miniOpenid = base64_decode($state);
|
|
if (empty($miniOpenid)) {
|
|
throw new CommonException('参数错误');
|
|
}
|
|
|
|
// 通过code获取access_token
|
|
$tokenUrl = 'https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query([
|
|
'appid' => $config['app_id'],
|
|
'secret' => $config['app_secret'],
|
|
'code' => $code,
|
|
'grant_type' => 'authorization_code'
|
|
]);
|
|
|
|
$tokenResponse = $this->httpGet($tokenUrl);
|
|
$tokenData = json_decode($tokenResponse, true);
|
|
|
|
if (!$tokenData || isset($tokenData['errcode'])) {
|
|
throw new CommonException('获取微信授权失败:' . ($tokenData['errmsg'] ?? '未知错误'));
|
|
}
|
|
|
|
$accessToken = $tokenData['access_token'];
|
|
$wechatOpenid = $tokenData['openid'];
|
|
|
|
// 获取用户信息
|
|
$userInfoUrl = 'https://api.weixin.qq.com/sns/userinfo?' . http_build_query([
|
|
'access_token' => $accessToken,
|
|
'openid' => $wechatOpenid,
|
|
'lang' => 'zh_CN'
|
|
]);
|
|
|
|
$userInfoResponse = $this->httpGet($userInfoUrl);
|
|
$userInfo = json_decode($userInfoResponse, true);
|
|
|
|
if (!$userInfo || isset($userInfo['errcode'])) {
|
|
throw new CommonException('获取微信用户信息失败:' . ($userInfo['errmsg'] ?? '未知错误'));
|
|
}
|
|
|
|
return [
|
|
'mini_openid' => $miniOpenid,
|
|
'wechat_openid' => $wechatOpenid,
|
|
'nickname' => $userInfo['nickname'] ?? '',
|
|
'avatar' => $userInfo['headimgurl'] ?? '',
|
|
'sex' => $userInfo['sex'] ?? 0
|
|
];
|
|
}
|
|
|
|
/**
|
|
* HTTP GET请求
|
|
* @param string $url
|
|
* @return string
|
|
* @throws \Exception
|
|
*/
|
|
private function httpGet(string $url)
|
|
{
|
|
$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);
|
|
$error = curl_error($ch);
|
|
curl_close($ch);
|
|
|
|
if ($response === false || !empty($error)) {
|
|
throw new CommonException('网络请求失败:' . $error);
|
|
}
|
|
|
|
if ($httpCode !== 200) {
|
|
throw new CommonException('网络请求失败,HTTP状态码:' . $httpCode);
|
|
}
|
|
|
|
return $response;
|
|
}
|
|
}
|
|
|