fetchWechatSession($code); $openid = trim((string) ($session['openid'] ?? '')); if ($openid === '') { throw new \Exception('微信登录失败,未获取到 openid', 500); } $now = time(); $user = NoteUser::findByOpenid($openid); $isNewUser = false; if (!$user) { $user = new NoteUser(); $user->openid = $openid; $user->created_at = $now; $isNewUser = true; } if (!$isNewUser && (int) $user->status === 0) { throw new \Exception('小程序账号已被禁用', 403); } $user->unionid = (string) ($session['unionid'] ?? ''); $user->session_key = (string) ($session['session_key'] ?? ''); $user->nickname = $nickname !== null ? trim($nickname) : (string) $user->nickname; $user->avatar_url = $avatarUrl !== null ? trim($avatarUrl) : (string) $user->avatar_url; $user->status = 1; $user->last_login_ip = (string) request()->ip(); $user->last_login_time = $now; $user->updated_at = $now; $user->deleted_at = 0; if (!$user->save()) { throw new \Exception('小程序用户登录保存失败', 500); } $token = Jwt::encode([ 'userid' => (int) $user->id, 'guard' => 'note', 'openid' => $user->openid, ]); $refreshToken = Jwt::encode([ 'userid' => (int) $user->id, 'guard' => 'note', 'type' => 'refresh', 'openid' => $user->openid, ]); return [ 'token' => $token, 'refresh_token' => $refreshToken, 'expires_in' => config('jwt.expire', 604800), 'user' => [ 'id' => (int) $user->id, 'member_id' => (int) ($user->member_id ?? 0), 'openid' => (string) $user->openid, 'nickname' => (string) $user->nickname, 'avatar_url' => (string) $user->avatar_url, 'mobile' => (string) $user->mobile, 'is_new_user' => $isNewUser, ], ]; } /** * 获取当前小程序用户信息 * * @param int $noteUserId * @return array * @throws \Exception */ public function getUserInfo(int $noteUserId): array { $user = NoteUser::findActiveById($noteUserId); if (!$user) { throw new \Exception('小程序用户不存在', 404); } return [ 'id' => (int) $user->id, 'member_id' => (int) ($user->member_id ?? 0), 'openid' => (string) $user->openid, 'nickname' => (string) $user->nickname, 'avatar_url' => (string) $user->avatar_url, 'mobile' => (string) $user->mobile, 'status' => (int) $user->status, 'last_login_time' => (int) $user->last_login_time, 'created_at' => (int) $user->created_at, ]; } /** * 请求微信 code2Session * * @param string $code * @return array * @throws \Exception */ private function fetchWechatSession(string $code): array { $appId = (string) env('WECHAT_MINI_APPID', ''); $appSecret = (string) env('WECHAT_MINI_SECRET', ''); if ($appId === '' || $appSecret === '') { throw new \Exception('缺少微信小程序配置 WECHAT_MINI_APPID / WECHAT_MINI_SECRET', 500); } $url = sprintf( 'https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code', urlencode($appId), urlencode($appSecret), urlencode($code) ); $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); $response = curl_exec($ch); $error = curl_error($ch); curl_close($ch); if ($response === false || $response === '' || $error) { throw new \Exception('微信登录请求失败', 502); } $data = json_decode($response, true); if (!is_array($data)) { throw new \Exception('微信登录响应解析失败', 502); } if (!empty($data['errcode'])) { throw new \Exception(sprintf('微信登录失败:%s', (string) ($data['errmsg'] ?? 'unknown error')), 400); } return $data; } }