智慧教务系统
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

<?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;
}
}