Files
appraisal_center_api/app/api/controller/WechatAuthController.php
2026-04-16 11:17:18 +08:00

206 lines
7.1 KiB
PHP

<?php
namespace app\api\controller;
use support\Request;
use app\common\model\User;
use app\common\model\WechatApp;
use app\common\model\UserWechatIdentity;
use app\common\service\AuthService;
class WechatAuthController
{
public function appList(Request $request)
{
$type = trim((string)$request->get('type', ''));
$query = WechatApp::query()->where('status', 1);
if ($type !== '') {
$query->where('type', $type);
}
$list = $query->select(['id', 'name', 'type', 'app_id'])->orderByDesc('id')->get();
return jsonResponse($list);
}
public function miniLogin(Request $request)
{
$appId = trim((string)$request->post('app_id', ''));
$code = trim((string)$request->post('code', ''));
if ($appId === '' || $code === '') {
return jsonResponse(null, '参数错误', 400);
}
try {
$app = $this->getApp($appId, 'mini');
} catch (\Throwable $e) {
return jsonResponse(null, $e->getMessage(), 400);
}
$secret = (string)($app->app_secret ?? '');
if ($secret === '') {
return jsonResponse(null, '未配置 app_secret', 400);
}
$url = 'https://api.weixin.qq.com/sns/jscode2session?appid=' . urlencode($appId) .
'&secret=' . urlencode($secret) .
'&js_code=' . urlencode($code) .
'&grant_type=authorization_code';
$res = $this->httpGetJson($url);
$openid = isset($res['openid']) ? trim((string)$res['openid']) : '';
$unionid = isset($res['unionid']) ? trim((string)$res['unionid']) : '';
if ($openid === '') {
$msg = $res['errmsg'] ?? '获取 openid 失败';
return jsonResponse(null, (string)$msg, 400);
}
$user = $this->resolveUserByWechatIdentity($appId, $openid, $unionid, 'mini');
if (intval($user->status) !== 1) {
return jsonResponse(null, '账号已禁用', 403);
}
$this->upsertIdentity($user->id, $appId, $openid, $unionid, 'mini');
$user->openid = $openid;
$user->save();
$token = AuthService::issueUserToken($user);
return jsonResponse([
'token' => $token,
'user' => $user,
'openid' => $openid,
'app_id' => $appId,
], '登录成功');
}
public function h5Login(Request $request)
{
$appId = trim((string)$request->post('app_id', ''));
$code = trim((string)$request->post('code', ''));
if ($appId === '' || $code === '') {
return jsonResponse(null, '参数错误', 400);
}
try {
$app = $this->getApp($appId, 'h5');
} catch (\Throwable $e) {
return jsonResponse(null, $e->getMessage(), 400);
}
$secret = (string)($app->app_secret ?? '');
if ($secret === '') {
return jsonResponse(null, '未配置 app_secret', 400);
}
$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . urlencode($appId) .
'&secret=' . urlencode($secret) .
'&code=' . urlencode($code) .
'&grant_type=authorization_code';
$res = $this->httpGetJson($url);
$openid = isset($res['openid']) ? trim((string)$res['openid']) : '';
$unionid = isset($res['unionid']) ? trim((string)$res['unionid']) : '';
if ($openid === '') {
$msg = $res['errmsg'] ?? '获取 openid 失败';
return jsonResponse(null, (string)$msg, 400);
}
$user = $this->resolveUserByWechatIdentity($appId, $openid, $unionid, 'h5');
if (intval($user->status) !== 1) {
return jsonResponse(null, '账号已禁用', 403);
}
$this->upsertIdentity($user->id, $appId, $openid, $unionid, 'h5');
$user->openid = $openid;
$user->save();
$token = AuthService::issueUserToken($user);
return jsonResponse([
'token' => $token,
'user' => $user,
'openid' => $openid,
'app_id' => $appId,
], '登录成功');
}
public function h5AuthorizeUrl(Request $request)
{
$appId = trim((string)$request->get('app_id', ''));
$redirectUri = trim((string)$request->get('redirect_uri', ''));
$scope = trim((string)$request->get('scope', 'snsapi_base'));
$state = trim((string)$request->get('state', ''));
if ($appId === '' || $redirectUri === '') {
return jsonResponse(null, '参数错误', 400);
}
if (!in_array($scope, ['snsapi_base', 'snsapi_userinfo'], true)) {
return jsonResponse(null, 'scope 不合法', 400);
}
try {
$this->getApp($appId, 'h5');
} catch (\Throwable $e) {
return jsonResponse(null, $e->getMessage(), 400);
}
$url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' . urlencode($appId) .
'&redirect_uri=' . urlencode($redirectUri) .
'&response_type=code&scope=' . urlencode($scope) .
'&state=' . urlencode($state) .
'#wechat_redirect';
return jsonResponse(['url' => $url]);
}
private function getApp(string $appId, string $type): WechatApp
{
$row = WechatApp::where('app_id', $appId)->where('status', 1)->first();
if (!$row) {
throw new \RuntimeException('AppID 未配置或已停用');
}
if ((string)$row->type !== $type) {
throw new \RuntimeException('AppID 类型不匹配');
}
return $row;
}
private function httpGetJson(string $url): array
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$body = curl_exec($ch);
if ($body === false) {
$err = curl_error($ch);
curl_close($ch);
throw new \RuntimeException('微信接口请求失败: ' . $err);
}
curl_close($ch);
return json_decode($body, true) ?: [];
}
private function resolveUserByWechatIdentity(string $appId, string $openid, string $unionid, string $scene): User
{
if ($unionid !== '') {
$identity = UserWechatIdentity::where('unionid', $unionid)->first();
if ($identity) {
$user = User::find($identity->user_id);
if ($user) return $user;
}
}
$identity = UserWechatIdentity::where('app_id', $appId)->where('openid', $openid)->first();
if ($identity) {
$user = User::find($identity->user_id);
if ($user) return $user;
}
return User::create([
'openid' => $openid,
'nickname' => $scene === 'mini' ? '小程序用户' : '微信用户',
'status' => 1,
]);
}
private function upsertIdentity(int $userId, string $appId, string $openid, string $unionid, string $scene): void
{
UserWechatIdentity::updateOrCreate(
['user_id' => $userId, 'app_id' => $appId],
['openid' => $openid, 'unionid' => $unionid ?: null, 'scene' => $scene]
);
}
}