From fcc7c97ca1d8d9bba77309cf380f1978a4c707b9 Mon Sep 17 00:00:00 2001 From: lion <120344285@qq.com> Date: Thu, 30 Jul 2026 11:08:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E4=BA=8C=E7=BB=B4=E7=A0=81?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wechat_qrcode_diag.php | 218 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 wechat_qrcode_diag.php diff --git a/wechat_qrcode_diag.php b/wechat_qrcode_diag.php new file mode 100644 index 0000000..90e93ca --- /dev/null +++ b/wechat_qrcode_diag.php @@ -0,0 +1,218 @@ +make(Illuminate\Contracts\Console\Kernel::class); + $kernel->bootstrap(); + $put('[bootstrap] ok'); +} catch (Throwable $e) { + $put('[bootstrap] FAIL: ' . $e->getMessage()); + output_and_exit($lines, $cli, 1); +} + +$put(''); +$put('--- config ---'); +$appId = (string) config('app.applet_appid'); +$secretCfg = (string) config('app.applet_secret'); +$put('APPLET_APPID=[' . $appId . ']'); +$put('APPLET_SECRET_set=' . ($secretCfg !== '' ? 'yes(len=' . strlen($secretCfg) . ')' : 'EMPTY')); +$put('CACHE_DRIVER=[' . config('cache.default') . ']'); +$put('APP_URL=[' . config('app.url') . ']'); +$put('LOG_CHANNEL=[' . config('logging.default') . ']'); +$put('LOG_LEVEL=[' . env('LOG_LEVEL', 'debug') . ']'); +$put('public.root=[' . config('filesystems.disks.public.root') . ']'); +$put('public.url=[' . config('filesystems.disks.public.url') . ']'); + +$put(''); +$put('--- cache dir ---'); +$cacheDir = storage_path('framework/cache/data'); +$put('cache_dir=' . $cacheDir); +$put('exists=' . (is_dir($cacheDir) ? 'yes' : 'no')); +$put('writable=' . (is_writable($cacheDir) ? 'yes' : 'no')); +if (is_dir($cacheDir)) { + $put('perms=' . substr(sprintf('%o', fileperms($cacheDir)), -4)); +} + +$put(''); +$put('--- laravel cache ---'); +try { + Illuminate\Support\Facades\Cache::put('wechat_qrcode_diag', 'ok', 60); + $val = Illuminate\Support\Facades\Cache::get('wechat_qrcode_diag'); + $has = Illuminate\Support\Facades\Cache::has('wechat_qrcode_diag'); + $put('put_get=' . ($val === 'ok' ? 'ok' : 'FAIL')); + $put('has=' . ($has ? 'yes' : 'no')); + Illuminate\Support\Facades\Cache::forget('wechat_qrcode_diag'); +} catch (Throwable $e) { + $put('cache_exception: ' . $e->getMessage()); +} + +$put(''); +$put('--- storage public ---'); +$publicRoot = (string) config('filesystems.disks.public.root'); +$qrDir = rtrim($publicRoot, '/') . '/course_qrcode'; +$qrPath = $qrDir . '/' . $courseId . '.png'; +$put('qr_dir=' . $qrDir); +$put('qr_dir_exists=' . (is_dir($qrDir) ? 'yes' : 'no')); +$put('qr_dir_writable=' . ((is_dir($qrDir) ? is_writable($qrDir) : is_writable(dirname($qrDir))) ? 'yes' : 'no')); +$put('qr_file_exists=' . (is_file($qrPath) ? 'yes' : 'no')); +if (is_file($qrPath)) { + $put('qr_file_size=' . filesize($qrPath)); +} +try { + if (!is_dir($qrDir)) { + mkdir($qrDir, 0755, true); + } + $testFile = $qrDir . '/_diag_write_test'; + file_put_contents($testFile, 'ok'); + $ok = is_file($testFile) && trim((string) file_get_contents($testFile)) === 'ok'; + @unlink($testFile); + $put('write_test=' . ($ok ? 'ok' : 'FAIL')); +} catch (Throwable $e) { + $put('write_test_exception: ' . $e->getMessage()); +} + +$put(''); +$put('--- easywechat token ---'); +if ($appId === '' || $secretCfg === '') { + $put('skip: missing appid/secret'); +} else { + try { + $mini = EasyWeChat\Factory::miniProgram([ + 'app_id' => $appId, + 'secret' => $secretCfg, + ]); + $token = $mini->access_token->getToken(true); + $put('token_ok=yes'); + $put('expires_in=' . ($token['expires_in'] ?? 'n/a')); + $put('token_prefix=' . substr((string) ($token['access_token'] ?? ''), 0, 10) . '...'); + } catch (Throwable $e) { + $put('token_FAIL: ' . $e->getMessage()); + output_summary($lines); + output_and_exit($lines, $cli, 1); + } + + $put(''); + $put('--- easywechat app_code ---'); + try { + $tmp = $mini->app_code->get('packages/course/detail?id=' . $courseId, [ + 'env_version' => 'release', + ]); + $body = $tmp instanceof EasyWeChat\Kernel\Http\StreamResponse + ? $tmp->getBodyContents() + : (string) $tmp; + $put('bytes=' . strlen($body)); + $isJson = strpos(ltrim($body), '{') === 0; + $put('is_json=' . ($isJson ? 'yes' : 'no')); + $put('looks_like_image=' . (strlen($body) > 100 && !$isJson ? 'yes' : 'no')); + if ($isJson) { + $put('wechat_body=' . substr($body, 0, 500)); + } else { + // 尝试落盘,验证完整链路 + if (!is_dir($qrDir)) { + mkdir($qrDir, 0755, true); + } + file_put_contents($qrPath, $body); + $put('saved_to=' . $qrPath); + $put('saved_ok=' . (is_file($qrPath) ? 'yes' : 'no')); + } + } catch (Throwable $e) { + $put('app_code_FAIL: ' . $e->getMessage()); + } +} + +$put(''); +$put('--- course model generate ---'); +try { + if (class_exists(App\Models\Course::class)) { + $course = App\Models\Course::find($courseId); + $put('course_exists=' . ($course ? 'yes' : 'no')); + if ($course) { + $put('course_name=' . ($course->name ?? '')); + $url = (new App\Models\Course())->getCourseQrcode($courseId, true); + $put('getCourseQrcode=[' . $url . ']'); + } + } else { + $put('Course model not found'); + } +} catch (Throwable $e) { + $put('course_generate_FAIL: ' . $e->getMessage()); +} + +$put(''); +output_summary($lines); +$put('DONE. 用完请删除本文件:wechat_qrcode_diag.php'); +output_and_exit($lines, $cli, 0); + +function output_summary(array &$lines) +{ + $text = implode("\n", $lines); + $lines[] = '--- summary ---'; + if (strpos($text, 'SECRET_set=EMPTY') !== false || strpos($text, 'APPLET_APPID=[]') !== false) { + $lines[] = '结论倾向: 微信配置未生效(APPLET_APPID/SECRET)'; + } elseif (strpos($text, 'cache_exception') !== false || preg_match('/has=no/', $text)) { + $lines[] = '结论倾向: 缓存不可用(易触发 Failed to cache access token)'; + } elseif (strpos($text, 'token_FAIL') !== false) { + $lines[] = '结论倾向: 获取 access_token 失败(配置/网络/IP白名单)'; + } elseif (strpos($text, 'is_json=yes') !== false) { + $lines[] = '结论倾向: 微信拒绝生成小程序码(看 wechat_body)'; + } elseif (strpos($text, 'write_test=FAIL') !== false || strpos($text, 'write_test_exception') !== false) { + $lines[] = '结论倾向: 二维码目录不可写'; + } elseif (strpos($text, 'getCourseQrcode=[http') !== false || strpos($text, 'saved_ok=yes') !== false) { + $lines[] = '结论倾向: 生成链路正常,可再试管理后台生成按钮'; + } else { + $lines[] = '结论倾向: 请把完整输出发开发排查'; + } +} + +function output_and_exit(array $lines, bool $cli, int $code): void +{ + $out = implode("\n", $lines) . "\n"; + echo $out; + + // 同时落盘,方便宝塔直接下载结果 + $resultFile = __DIR__ . '/wechat_qrcode_diag_result.txt'; + @file_put_contents($resultFile, $out); + if (!$cli) { + echo "\n结果已写入: wechat_qrcode_diag_result.txt\n"; + } else { + fwrite(STDERR, "结果已写入: {$resultFile}\n"); + } + exit($code); +}