diff --git a/app/Cache/Redis/Common/CommonRedisKey.php b/app/Cache/Redis/Common/CommonRedisKey.php index 8312f79..ba83e74 100644 --- a/app/Cache/Redis/Common/CommonRedisKey.php +++ b/app/Cache/Redis/Common/CommonRedisKey.php @@ -39,4 +39,13 @@ class CommonRedisKey { return '__system:activate:site:list'; } + + /** + * 获取微信access_token + * @return string + */ + public static function getWxAccessToken(): string + { + return '__system:wx:AccessToken:'; + } } \ No newline at end of file diff --git a/app/Controller/Api/UserController.php b/app/Controller/Api/UserController.php index f65ad89..13ff4c9 100644 --- a/app/Controller/Api/UserController.php +++ b/app/Controller/Api/UserController.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace App\Controller\Api; use App\Middleware\Api\JwtAuthMiddleware; +use App\Request\Api\UserRequest; use App\Service\Api\User\BindPhoneByWxService; use Hyperf\HttpServer\Annotation\Controller; use Hyperf\HttpServer\Annotation\Middlewares; @@ -19,7 +20,7 @@ class UserController { #[RequestMapping(path: 'bind_phone/wx_code',methods: 'post')] #[Scene(scene: 'bind_phone_wx')] - public function bind_phone_by_wx_code() + public function bind_phone_by_wx_code(UserRequest $request) { return (new BindPhoneByWxService)->handle(); } diff --git a/app/Request/Api/UserRequest.php b/app/Request/Api/UserRequest.php new file mode 100644 index 0000000..637a666 --- /dev/null +++ b/app/Request/Api/UserRequest.php @@ -0,0 +1,28 @@ + 'required|string', + ]; + } +} diff --git a/app/Service/Api/User/BindPhoneByWxService.php b/app/Service/Api/User/BindPhoneByWxService.php index 0667317..9a2ae15 100644 --- a/app/Service/Api/User/BindPhoneByWxService.php +++ b/app/Service/Api/User/BindPhoneByWxService.php @@ -14,16 +14,31 @@ use App\Exception\ErrException; use App\Service\Api\BaseService; use App\Service\ServiceTrait\Api\GetUserInfoTrait; use App\Service\ServiceTrait\Api\WxMiniTrait; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; class BindPhoneByWxService extends BaseService { - use GetUserInfoTrait,WxMiniTrait; + use GetUserInfoTrait, + WxMiniTrait; - public function handle() + /** + * @return array + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + public function handle(): array { $this->checkBindPhone($this->userId); + $wxPhone = $this->jsCodeGetPhoneNumber($this->request->input('code')); + if (empty($wxPhone['phone_info']['purePhoneNumber'])) throw new ErrException('微信手机号查询失败,请联系人工客服处理'); + + $userInfo = $this->getUserInfo($this->userId); + + $userInfo->phone = $wxPhone['phone_info']['purePhoneNumber']; + if (!$userInfo->save()) throw new ErrException('绑定手机号失败,请联系人工客服处理'); return $this->return->success(); } diff --git a/app/Service/ServiceTrait/Api/WxMiniTrait.php b/app/Service/ServiceTrait/Api/WxMiniTrait.php index 952576a..2ea2c04 100644 --- a/app/Service/ServiceTrait/Api/WxMiniTrait.php +++ b/app/Service/ServiceTrait/Api/WxMiniTrait.php @@ -10,6 +10,9 @@ declare(strict_types=1); namespace App\Service\ServiceTrait\Api; +use App\Cache\Redis\Api\ApiRedisKey; +use App\Cache\Redis\Common\CommonRedisKey; +use App\Cache\Redis\RedisCache; use App\Exception\ErrException; use App\Lib\Log; use GuzzleHttp\Exception\GuzzleException; @@ -35,19 +38,90 @@ trait WxMiniTrait #[Inject] protected Log $log; + /** + * 注入缓存类 + * @var RedisCache + */ + #[Inject] + protected RedisCache $redisCache; + /** * baseUri * @var string */ protected string $BaseUri = 'https://api.weixin.qq.com'; + private string $appId = ''; + private string $appSecret = ''; + public function __construct() + { + $this->appId = config('system.wx_appid'); + } + + /** + * @return false|mixed|\Redis|string + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + protected function getAccessTokenCache(): mixed + { + $key = CommonRedisKey::getWxAccessToken(); + + if ($this->redisCache->exists($key)) { + return $this->redisCache->get($key) ?? ''; + } + + $data = $this->getAccessToken(); + + if (empty($data) || empty($data['access_token']) || empty($data['expires_in'])) return ''; + + $this->redisCache->set($key, $data['access_token'], $data['expires_in'] - 10); + + return $data['access_token']; + } + + /** + * 获取 access Token + * @return mixed + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + private function getAccessToken(): mixed + { + $url = sprintf( + '/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s', + config('system.wx_appid'), + config('system.wx_secret') + ); + + try { + $tencentResponse = $this->clientFactory->create([ + 'base_uri' => $this->BaseUri, + 'timeout' => 5 + ])->get($url); + + $contents = $tencentResponse->getBody()->getContents(); + + $this->log->callbackLog(__class__.':微信服务器返回getAccessToken信息:'.json_encode($contents)); + + $res = json_decode($contents,true); + + if (!empty($res['errcode']) && $res['errcode'] != 0) throw new ErrException($res['errmsg'] ?? '系统繁忙'); + + return $res; + }catch (GuzzleException $e) { + $this->log->debug(__CLASS__.':debug:'.$e->getMessage()); + throw new ErrException($e->getMessage()); + } + } + /** * @param string $code * @return mixed * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function jsCodeGetOpenId(string $code): mixed + protected function jsCodeGetOpenId(string $code): mixed { $url = sprintf( '/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code', @@ -77,8 +151,46 @@ trait WxMiniTrait } } - public function jsCodeGetPhoneNumber(string $code): mixed + /** + * @param string $code + * @return mixed + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + protected function jsCodeGetPhoneNumber(string $code): mixed { + $url = sprintf( + '/wxa/business/getuserphonenumber?access_token=%s', + $this->getAccessTokenCache() + ); + $params = [ + 'code' => $code, + ]; + + try { + $wxResponse = $this->clientFactory->create([ + 'base_uri' => $this->BaseUri, + 'timeout' => 5 + ])->post($url,[ + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'body' => json_encode($params) + ]); + + $contents = $wxResponse->getBody()->getContents(); + + $this->log->callbackLog(__class__.':微信服务器返回get_phone_number信息:'.json_encode($contents)); + + $res = json_decode($contents,true); + + if (!empty($res['errcode']) && $res['errcode'] != 0) throw new ErrException($res['errmsg'] ?? '系统繁忙'); + + return $res; + }catch (GuzzleException $e){ + $this->log->debug(__CLASS__.':debug:'.$e->getMessage()); + throw new ErrException($e->getMessage()); + } } } \ No newline at end of file