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] ); } }