100 lines
3.2 KiB
PHP
100 lines
3.2 KiB
PHP
<?php
|
||
|
||
namespace App\Services;
|
||
|
||
use App\Exceptions\RepositoryException;
|
||
use EasyWeChat\Kernel\Exceptions\HttpException;
|
||
use EasyWeChat\MiniApp\Application;
|
||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||
|
||
/**
|
||
* 微信小程序服务(基于 EasyWeChat 6.x)
|
||
*
|
||
* 封装 code2Session / 手机号解密;配置读取 site_config('wechatMini')
|
||
* (后台「小程序设置」面板维护,存 sys_site_config 表)。
|
||
*
|
||
* 测试策略:通过 setHttpClient() 注入 Symfony MockHttpClient 拦截微信 HTTP 调用。
|
||
*/
|
||
class WechatService
|
||
{
|
||
private ?Application $app = null;
|
||
|
||
private ?HttpClientInterface $httpClient = null;
|
||
|
||
/**
|
||
* 注入自定义 HttpClient(测试注入 MockHttpClient;注入后强制重建 Application)
|
||
*/
|
||
public function setHttpClient(HttpClientInterface $httpClient): void
|
||
{
|
||
$this->httpClient = $httpClient;
|
||
$this->app = null;
|
||
}
|
||
|
||
/**
|
||
* code2Session:小程序 wx.login 的 code 换取 openid / session_key
|
||
*
|
||
* @param string $code wx.login 返回的临时登录凭证
|
||
* @return array{openid: string, session_key: string, unionid?: string}
|
||
*/
|
||
public function code2Session(string $code): array
|
||
{
|
||
try {
|
||
/** @var array{openid: string, session_key: string, unionid?: string} $session */
|
||
$session = $this->app()->getUtils()->codeToSession($code);
|
||
} catch (HttpException|TransportExceptionInterface $e) {
|
||
throw new RepositoryException('微信登录失败:' . $e->getMessage());
|
||
}
|
||
|
||
return $session;
|
||
}
|
||
|
||
/**
|
||
* 获取手机号:wx.getPhoneNumber 的 phoneCode 换取手机号
|
||
*
|
||
* @param string $phoneCode 手机号授权事件返回的动态令牌
|
||
* @return string 用户手机号
|
||
*/
|
||
public function getPhone(string $phoneCode): string
|
||
{
|
||
try {
|
||
$result = $this->app()->getUtils()->getPhoneNumber($phoneCode);
|
||
} catch (HttpException|TransportExceptionInterface $e) {
|
||
throw new RepositoryException('获取手机号失败:' . $e->getMessage());
|
||
}
|
||
|
||
$phone = (string) ($result['phone_info']['phoneNumber']
|
||
?? $result['phone_info']['purePhoneNumber']
|
||
?? '');
|
||
if ($phone === '') {
|
||
throw new RepositoryException('获取手机号失败:微信未返回有效手机号');
|
||
}
|
||
|
||
return $phone;
|
||
}
|
||
|
||
/**
|
||
* EasyWeChat 小程序应用实例(懒构建单例)
|
||
*/
|
||
protected function app(): Application
|
||
{
|
||
if ($this->app === null) {
|
||
$config = (array) site_config('wechatMini', []);
|
||
if (empty($config['appid']) || empty($config['secret'])) {
|
||
throw new RepositoryException('微信小程序尚未配置(WECHAT_MINI_APPID / WECHAT_MINI_SECRET)');
|
||
}
|
||
|
||
$this->app = new Application([
|
||
'app_id' => (string) $config['appid'],
|
||
'secret' => (string) $config['secret'],
|
||
]);
|
||
|
||
if ($this->httpClient !== null) {
|
||
$this->app->setHttpClient($this->httpClient);
|
||
}
|
||
}
|
||
|
||
return $this->app;
|
||
}
|
||
}
|