mirror of
https://gitee.com/koogua/course-tencent-cloud.git
synced 2025-06-26 12:23:06 +08:00
Merge branch 'koogua/v1.4.5' into demo
This commit is contained in:
commit
7b6edab272
52
app/Http/Api/Controllers/ConnectController.php
Normal file
52
app/Http/Api/Controllers/ConnectController.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Api\Controllers;
|
||||
|
||||
use App\Http\Api\Services\Connect as ConnectService;
|
||||
use App\Models\Connect as ConnectModel;
|
||||
|
||||
/**
|
||||
* @RoutePrefix("/api/oauth")
|
||||
*/
|
||||
class ConnectController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @Get("/wechat", name="api.oauth.wechat")
|
||||
*/
|
||||
public function wechatAction()
|
||||
{
|
||||
$service = new ConnectService();
|
||||
|
||||
$url = $service->getAuthorizeUrl(ConnectModel::PROVIDER_WECHAT);
|
||||
|
||||
return $this->response->redirect($url, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/wechat/callback", name="api.oauth.wechat_callback")
|
||||
*/
|
||||
public function wechatCallbackAction()
|
||||
{
|
||||
$service = new ConnectService();
|
||||
|
||||
$data = $service->handleCallback(ConnectModel::PROVIDER_WECHAT);
|
||||
|
||||
$location = kg_h5_index_url() . '?' . http_build_query($data);
|
||||
|
||||
return $this->response->redirect($location, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/wechat/info", name="api.oauth.wechat_info")
|
||||
*/
|
||||
public function wechatInfoAction()
|
||||
{
|
||||
$service = new ConnectService();
|
||||
|
||||
$wechat = $service->getWechatInfo();
|
||||
|
||||
return $this->jsonSuccess(['wechat' => $wechat]);
|
||||
}
|
||||
|
||||
}
|
@ -54,6 +54,18 @@ class TradeController extends Controller
|
||||
return $this->jsonSuccess($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/mini/create", name="api.trade.mini_create")
|
||||
*/
|
||||
public function createMiniTradeAction()
|
||||
{
|
||||
$service = new TradeService();
|
||||
|
||||
$content = $service->createMiniTrade();
|
||||
|
||||
return $this->jsonSuccess($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/app/create", name="api.trade.app_create")
|
||||
*/
|
||||
@ -61,7 +73,7 @@ class TradeController extends Controller
|
||||
{
|
||||
$service = new TradeService();
|
||||
|
||||
$content = $service->createMpTrade();
|
||||
$content = $service->createAppTrade();
|
||||
|
||||
return $this->jsonSuccess($content);
|
||||
}
|
||||
|
190
app/Http/Api/Services/Connect.php
Normal file
190
app/Http/Api/Services/Connect.php
Normal file
@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Api\Services;
|
||||
|
||||
use App\Models\Connect as ConnectModel;
|
||||
use App\Models\User as UserModel;
|
||||
use App\Repos\Connect as ConnectRepo;
|
||||
use App\Repos\User as UserRepo;
|
||||
use App\Services\Auth\Api as ApiAuthService;
|
||||
use App\Services\Logic\Notice\AccountLogin as AccountLoginNoticeService;
|
||||
use App\Services\OAuth\QQ as QQAuth;
|
||||
use App\Services\OAuth\WeChat as WeChatAuth;
|
||||
use App\Services\OAuth\WeiBo as WeiBoAuth;
|
||||
use Exception;
|
||||
|
||||
class Connect extends Service
|
||||
{
|
||||
|
||||
public function getWechatInfo()
|
||||
{
|
||||
return [
|
||||
'auth_url' => kg_full_url(['for' => 'api.oauth.wechat']),
|
||||
'auto_login' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
public function handleCallback($provider)
|
||||
{
|
||||
$code = $this->request->getQuery('code');
|
||||
$state = $this->request->getQuery('state');
|
||||
|
||||
$openUser = $this->getOpenUserInfo($code, $state, $provider);
|
||||
$relation = $this->getConnectRelation($openUser['id'], $provider);
|
||||
|
||||
$token = null;
|
||||
|
||||
if ($relation) {
|
||||
|
||||
$userRepo = new UserRepo();
|
||||
|
||||
$user = $userRepo->findById($relation->user_id);
|
||||
|
||||
$auth = new ApiAuthService();
|
||||
|
||||
$token = $auth->saveAuthInfo($user);
|
||||
|
||||
$this->handleLoginNotice($user);
|
||||
}
|
||||
|
||||
return [
|
||||
'openid' => $openUser['id'],
|
||||
'token' => $token,
|
||||
];
|
||||
}
|
||||
|
||||
public function getAuthorizeUrl($provider)
|
||||
{
|
||||
$auth = $this->getConnectAuth($provider);
|
||||
|
||||
return $auth->getAuthorizeUrl();
|
||||
}
|
||||
|
||||
public function getOpenUserInfo($code, $state, $provider)
|
||||
{
|
||||
$auth = $this->getConnectAuth($provider);
|
||||
|
||||
$auth->checkState($state);
|
||||
|
||||
$token = $auth->getAccessToken($code);
|
||||
|
||||
$openId = $auth->getOpenId($token);
|
||||
|
||||
return $auth->getUserInfo($token, $openId);
|
||||
}
|
||||
|
||||
public function getConnectRelation($openId, $provider)
|
||||
{
|
||||
$connectRepo = new ConnectRepo();
|
||||
|
||||
return $connectRepo->findByOpenId($openId, $provider);
|
||||
}
|
||||
|
||||
public function getConnectAuth($provider)
|
||||
{
|
||||
$auth = null;
|
||||
|
||||
switch ($provider) {
|
||||
case ConnectModel::PROVIDER_QQ:
|
||||
$auth = $this->getQQAuth();
|
||||
break;
|
||||
case ConnectModel::PROVIDER_WEIBO:
|
||||
$auth = $this->getWeiBoAuth();
|
||||
break;
|
||||
case ConnectModel::PROVIDER_WECHAT:
|
||||
$auth = $this->getWeChatAuth();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$auth) {
|
||||
throw new Exception('Invalid OAuth Provider');
|
||||
}
|
||||
|
||||
return $auth;
|
||||
}
|
||||
|
||||
protected function getQQAuth()
|
||||
{
|
||||
$settings = $this->getSettings('oauth.qq');
|
||||
|
||||
$settings['redirect_uri'] = kg_full_url(['for' => 'api.oauth.qq_callback']);
|
||||
|
||||
return new QQAuth(
|
||||
$settings['client_id'],
|
||||
$settings['client_secret'],
|
||||
$settings['redirect_uri']
|
||||
);
|
||||
}
|
||||
|
||||
protected function getWeChatAuth()
|
||||
{
|
||||
/**
|
||||
* 使用的是微信公众号网页授权登录功能
|
||||
*/
|
||||
$settings = $this->getSettings('wechat.oa');
|
||||
|
||||
$settings['redirect_uri'] = kg_full_url(['for' => 'api.oauth.wechat_callback']);
|
||||
|
||||
return new WeChatAuth(
|
||||
$settings['app_id'],
|
||||
$settings['app_secret'],
|
||||
$settings['redirect_uri']
|
||||
);
|
||||
}
|
||||
|
||||
protected function getWeiBoAuth()
|
||||
{
|
||||
$settings = $this->getSettings('oauth.weibo');
|
||||
|
||||
$settings['redirect_uri'] = kg_full_url(['for' => 'api.oauth.weibo_callback']);
|
||||
|
||||
return new WeiBoAuth(
|
||||
$settings['client_id'],
|
||||
$settings['client_secret'],
|
||||
$settings['redirect_uri']
|
||||
);
|
||||
}
|
||||
|
||||
protected function handleConnectRelation(UserModel $user, array $openUser)
|
||||
{
|
||||
$connectRepo = new ConnectRepo();
|
||||
|
||||
$connect = $connectRepo->findByOpenId($openUser['id'], $openUser['provider']);
|
||||
|
||||
if ($connect) {
|
||||
|
||||
$connect->open_name = $openUser['name'];
|
||||
$connect->open_avatar = $openUser['avatar'];
|
||||
|
||||
if ($connect->user_id != $user->id) {
|
||||
$connect->user_id = $user->id;
|
||||
}
|
||||
|
||||
if ($connect->deleted == 1) {
|
||||
$connect->deleted = 0;
|
||||
}
|
||||
|
||||
$connect->update();
|
||||
|
||||
} else {
|
||||
|
||||
$connect = new ConnectModel();
|
||||
|
||||
$connect->user_id = $user->id;
|
||||
$connect->open_id = $openUser['id'];
|
||||
$connect->open_name = $openUser['name'];
|
||||
$connect->open_avatar = $openUser['avatar'];
|
||||
$connect->provider = $openUser['provider'];
|
||||
|
||||
$connect->create();
|
||||
}
|
||||
}
|
||||
|
||||
protected function handleLoginNotice(UserModel $user)
|
||||
{
|
||||
$service = new AccountLoginNoticeService();
|
||||
|
||||
$service->createTask($user);
|
||||
}
|
||||
|
||||
}
|
@ -75,6 +75,45 @@ class Trade extends Service
|
||||
{
|
||||
$post = $this->request->getPost();
|
||||
|
||||
$order = $this->checkOrderBySn($post['order_sn']);
|
||||
|
||||
$user = $this->getLoginUser();
|
||||
|
||||
$channel = TradeModel::CHANNEL_WXPAY;
|
||||
|
||||
$trade = new TradeModel();
|
||||
|
||||
$trade->subject = $order->subject;
|
||||
$trade->amount = $order->amount;
|
||||
$trade->channel = $channel;
|
||||
$trade->order_id = $order->id;
|
||||
$trade->owner_id = $user->id;
|
||||
|
||||
$trade->create();
|
||||
|
||||
$wxpay = new Wxpay();
|
||||
|
||||
$response = $wxpay->mp($trade, $post['open_id']);
|
||||
|
||||
$payment = [
|
||||
'appId' => $response->appId,
|
||||
'timeStamp' => $response->timeStamp,
|
||||
'nonceStr' => $response->nonceStr,
|
||||
'package' => $response->package,
|
||||
'signType' => $response->signType,
|
||||
'paySign' => $response->paySign,
|
||||
];
|
||||
|
||||
return [
|
||||
'trade' => $this->handleTradeInfo($trade->sn),
|
||||
'payment' => $payment,
|
||||
];
|
||||
}
|
||||
|
||||
public function createMiniTrade()
|
||||
{
|
||||
$post = $this->request->getPost();
|
||||
|
||||
$validator = new ClientValidator();
|
||||
|
||||
$platform = $this->getPlatform();
|
||||
@ -120,7 +159,7 @@ class Trade extends Service
|
||||
|
||||
public function createAppTrade()
|
||||
{
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function getPlatform()
|
||||
|
@ -66,7 +66,7 @@
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro live_status_info(lesson) %}
|
||||
{% if lesson.attrs.start_time < time() and lesson.attrs.end_time > time() %}
|
||||
{% if lesson.attrs.stream.status == 'active' %}
|
||||
<span class="active">{{ date('m月d日 H:i',lesson.attrs.start_time) }} 直播中</span>
|
||||
{% elseif lesson.attrs.start_time > time() %}
|
||||
<span class="pending">{{ date('m月d日 H:i',lesson.attrs.start_time) }} 倒计时</span>
|
||||
|
@ -16,7 +16,7 @@ class AppInfo
|
||||
|
||||
protected $link = 'https://koogua.com';
|
||||
|
||||
protected $version = '1.4.4';
|
||||
protected $version = '1.4.5';
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
|
@ -707,4 +707,14 @@ function kg_full_url($uri, $args = null)
|
||||
$baseUrl = kg_site_url();
|
||||
|
||||
return $baseUrl . $url->get($uri, $args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取H5首页地址
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function kg_h5_index_url()
|
||||
{
|
||||
return kg_site_url() . '/h5/#/pages/index/index';
|
||||
}
|
||||
|
@ -7,12 +7,15 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Phalcon\Mvc\Model\Behavior\SoftDelete;
|
||||
|
||||
class Connect extends Model
|
||||
{
|
||||
|
||||
const PROVIDER_QQ = 1; // QQ
|
||||
const PROVIDER_WEIXIN = 2; // 微信
|
||||
const PROVIDER_WEIBO = 3; // 微博
|
||||
const PROVIDER_WEIXIN = 2; // 微信扫码
|
||||
const PROVIDER_WEIBO = 3; // 新浪微博
|
||||
const PROVIDER_WECHAT = 4; // 公众号网页
|
||||
|
||||
/**
|
||||
* 主键编号
|
||||
@ -28,6 +31,13 @@ class Connect extends Model
|
||||
*/
|
||||
public $user_id = 0;
|
||||
|
||||
/**
|
||||
* 联合ID
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $union_id = '';
|
||||
|
||||
/**
|
||||
* 开放ID
|
||||
*
|
||||
@ -56,6 +66,13 @@ class Connect extends Model
|
||||
*/
|
||||
public $provider = 0;
|
||||
|
||||
/**
|
||||
* 删除标识
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $deleted = 0;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*
|
||||
@ -75,6 +92,18 @@ class Connect extends Model
|
||||
return 'kg_connect';
|
||||
}
|
||||
|
||||
public function initialize()
|
||||
{
|
||||
parent::initialize();
|
||||
|
||||
$this->addBehavior(
|
||||
new SoftDelete([
|
||||
'field' => 'deleted',
|
||||
'value' => 1,
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
public function beforeCreate()
|
||||
{
|
||||
$this->create_time = time();
|
||||
|
@ -113,6 +113,7 @@ class QQ extends OAuth
|
||||
$userInfo['name'] = $data['nickname'];
|
||||
$userInfo['avatar'] = $data['figureurl'];
|
||||
$userInfo['provider'] = ConnectModel::PROVIDER_QQ;
|
||||
$userInfo['unionid'] = '';
|
||||
|
||||
return $userInfo;
|
||||
}
|
||||
|
94
app/Services/OAuth/WeChat.php
Normal file
94
app/Services/OAuth/WeChat.php
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\OAuth;
|
||||
|
||||
use App\Models\Connect as ConnectModel;
|
||||
use App\Services\OAuth;
|
||||
|
||||
class WeChat extends OAuth
|
||||
{
|
||||
|
||||
const AUTHORIZE_URL = 'https://open.weixin.qq.com/connect/oauth2/authorize';
|
||||
const ACCESS_TOKEN_URL = 'https://api.weixin.qq.com/sns/oauth2/access_token';
|
||||
const USER_INFO_URL = 'https://api.weixin.qq.com/sns/userinfo';
|
||||
|
||||
public function getAuthorizeUrl()
|
||||
{
|
||||
/**
|
||||
* 参数强制要求按特定顺序排列
|
||||
*/
|
||||
$params = [
|
||||
'appid' => $this->clientId,
|
||||
'redirect_uri' => $this->redirectUri,
|
||||
'response_type' => 'code',
|
||||
'scope' => 'snsapi_userinfo',
|
||||
'state' => $this->getState(),
|
||||
];
|
||||
|
||||
return self::AUTHORIZE_URL . '?' . http_build_query($params) . '#wechat_redirect';
|
||||
}
|
||||
|
||||
public function getAccessToken($code)
|
||||
{
|
||||
$params = [
|
||||
'code' => $code,
|
||||
'appid' => $this->clientId,
|
||||
'secret' => $this->clientSecret,
|
||||
'grant_type' => 'authorization_code',
|
||||
];
|
||||
|
||||
$response = $this->httpPost(self::ACCESS_TOKEN_URL, $params);
|
||||
|
||||
$this->accessToken = $this->parseAccessToken($response);
|
||||
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
public function getOpenId($accessToken = null)
|
||||
{
|
||||
return $this->openId;
|
||||
}
|
||||
|
||||
public function getUserInfo($accessToken, $openId)
|
||||
{
|
||||
$params = [
|
||||
'access_token' => $accessToken,
|
||||
'openid' => $openId,
|
||||
];
|
||||
|
||||
$response = $this->httpGet(self::USER_INFO_URL, $params);
|
||||
|
||||
return $this->parseUserInfo($response);
|
||||
}
|
||||
|
||||
private function parseAccessToken($response)
|
||||
{
|
||||
$data = json_decode($response, true);
|
||||
|
||||
if (isset($data['errcode']) && $data['errcode'] != 0) {
|
||||
throw new \Exception("Fetch Access Token Failed:{$response}");
|
||||
}
|
||||
|
||||
$this->openId = $data['openid'];
|
||||
|
||||
return $data['access_token'];
|
||||
}
|
||||
|
||||
private function parseUserInfo($response)
|
||||
{
|
||||
$data = json_decode($response, true);
|
||||
|
||||
if (isset($data['errcode']) && $data['errcode'] != 0) {
|
||||
throw new \Exception("Fetch User Info Failed:{$response}");
|
||||
}
|
||||
|
||||
$userInfo['id'] = $data['openid'];
|
||||
$userInfo['name'] = $data['nickname'];
|
||||
$userInfo['avatar'] = $data['headimgurl'];
|
||||
$userInfo['unionid'] = $data['unionid'] ?? '';
|
||||
$userInfo['provider'] = ConnectModel::PROVIDER_WECHAT;
|
||||
|
||||
return $userInfo;
|
||||
}
|
||||
|
||||
}
|
@ -88,6 +88,7 @@ class WeiBo extends OAuth
|
||||
$userInfo['name'] = $data['name'];
|
||||
$userInfo['avatar'] = $data['profile_image_url'];
|
||||
$userInfo['provider'] = ConnectModel::PROVIDER_WEIBO;
|
||||
$userInfo['unionid'] = '';
|
||||
|
||||
return $userInfo;
|
||||
}
|
||||
|
@ -87,6 +87,7 @@ class WeiXin extends OAuth
|
||||
$userInfo['id'] = $data['openid'];
|
||||
$userInfo['name'] = $data['nickname'];
|
||||
$userInfo['avatar'] = $data['headimgurl'];
|
||||
$userInfo['unionid'] = $data['unionid'] ?? '';
|
||||
$userInfo['provider'] = ConnectModel::PROVIDER_WEIXIN;
|
||||
|
||||
return $userInfo;
|
||||
|
@ -121,6 +121,37 @@ class Wxpay extends PayService
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 公众号支付
|
||||
*
|
||||
* @param TradeModel $trade
|
||||
* @param string $openId
|
||||
* @return Collection|bool
|
||||
*/
|
||||
public function mp(TradeModel $trade, $openId)
|
||||
{
|
||||
try {
|
||||
|
||||
$result = $this->gateway->mp([
|
||||
'out_trade_no' => $trade->sn,
|
||||
'total_fee' => 100 * $trade->amount,
|
||||
'body' => $trade->subject,
|
||||
'openid' => $openId,
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
|
||||
Log::error('Wxpay MP Exception', [
|
||||
'code' => $e->getCode(),
|
||||
'message' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
$result = false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序支付
|
||||
*
|
||||
|
56
db/migrations/20210917093354.php
Normal file
56
db/migrations/20210917093354.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
|
||||
* @license https://opensource.org/licenses/GPL-2.0
|
||||
* @link https://www.koogua.com
|
||||
*/
|
||||
|
||||
use Phinx\Db\Adapter\MysqlAdapter;
|
||||
|
||||
class V20210917093354 extends Phinx\Migration\AbstractMigration
|
||||
{
|
||||
|
||||
public function up()
|
||||
{
|
||||
$this->alterConnectTable();
|
||||
$this->alterWechatSubscribeTable();
|
||||
}
|
||||
|
||||
protected function alterConnectTable()
|
||||
{
|
||||
$this->table('kg_connect')
|
||||
->addColumn('deleted', 'integer', [
|
||||
'null' => false,
|
||||
'default' => '0',
|
||||
'limit' => MysqlAdapter::INT_REGULAR,
|
||||
'signed' => false,
|
||||
'comment' => '删除标识',
|
||||
'after' => 'provider',
|
||||
])
|
||||
->save();
|
||||
}
|
||||
|
||||
protected function alterWechatSubscribeTable()
|
||||
{
|
||||
$this->table('kg_wechat_subscribe')
|
||||
->addColumn('union_id', 'string', [
|
||||
'null' => false,
|
||||
'default' => '',
|
||||
'limit' => 64,
|
||||
'collation' => 'utf8mb4_general_ci',
|
||||
'encoding' => 'utf8mb4',
|
||||
'comment' => '联合ID',
|
||||
'after' => 'open_id',
|
||||
])
|
||||
->addColumn('deleted', 'integer', [
|
||||
'null' => false,
|
||||
'default' => '0',
|
||||
'limit' => MysqlAdapter::INT_REGULAR,
|
||||
'signed' => false,
|
||||
'comment' => '删除标识',
|
||||
'after' => 'open_id',
|
||||
])
|
||||
->save();
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user