Files
vibe_coding/docs/runbooks/backend-debug.md
2026-03-05 21:27:11 +08:00

305 lines
7.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Backend Debug Runbook
## 常见问题排查
### 1. 登录返回 401 "账号或密码错误"
#### 症状
```bash
curl -X POST http://127.0.0.1:9501/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "superadmin", "password": "Admin@2026"}'
# 返回: {"code": 401, "message": "账号或密码错误"}
```
#### 排查步骤
1. **检查数据库是否有用户**
```bash
docker exec hyperf-mysql mysql -uroot -p123456 case_db \
-e "SELECT id, username, phone FROM auth_users WHERE deleted_at IS NULL;"
```
2. **如果用户不存在,运行 Seeder**
```bash
docker exec hyperf-skeleton php bin/hyperf.php db:seed --class=DatabaseSeeder
```
3. **验证密码比对逻辑**
```bash
# 检查 User Model 是否使用 password_hash 和 password_verify
# 确保 Seeder 中的密码也是用 password_hash 加密的
```
### 2. 返回 500 "Internal Server Error" - env() 未定义
#### 症状
```bash
curl -X POST http://127.0.0.1:9501/api/auth/sms/send \
-H "Content-Type: application/json" \
-d '{"phone": "18970867739", "purpose": "login"}'
# 返回: {"code": 500, "message": "Internal Server Error"}
```
#### 日志错误
```
[ERROR] Call to undefined function env()[43] in /opt/www/modules/Auth/src/Service/SmsService.php
[ERROR] #0 /opt/www/runtime/container/proxy/Modules_Auth_Http_Controller_Api_SmsController.proxy.php(42):
Modules\Auth\Service\SmsService->send()
```
#### 根因
PHP CLI 和 Swoole Worker 环境中**不提供全局 `env()` 函数**(这是 Laravel 特有的),会导致 `Call to undefined function env()` 错误。
#### 解决方案
**修改前(错误)**
```php
// ❌ 在 Service/Middleware 中直接使用 env()
if (env('APP_ENV', 'development') !== 'production') {
// ...
}
$secret = env('JWT_SECRET', 'default');
```
**修改后(正确)**
```php
// ✅ 使用 Hyperf 的 config() 函数读取配置
$appEnv = \Hyperf\Config\config('app.env', 'development');
if ($appEnv !== 'production') {
// ...
}
$secret = \Hyperf\Config\config('jwt.secret', 'default-secret');
```
**配置文件正确写法**(在配置文件中可以使用 env()
```php
// config/autoload/jwt.php
use function Hyperf\Support\env;
return [
'secret' => env('JWT_SECRET', 'dev-fallback-secret'),
'ttl' => (int) env('JWT_TTL', 7200),
];
```
**常见错误场景**
```php
// ❌ Service 层中使用 env()
class SmsService {
public function send() {
if (env('APP_ENV') !== 'production') { // 报错
// ...
}
}
}
// ❌ Middleware 层中使用 env()
class JwtAuthMiddleware {
public function process() {
$secret = env('JWT_SECRET'); // 报错
}
}
// ❌ 即使添加命名空间前缀也不行
if (\env('APP_ENV') !== 'production') { // 仍然报错
```
**正确实践**
```php
// ✅ Service 层使用 config()
use Hyperf\Config\config;
class SmsService {
public function send() {
$appEnv = config('app.env', 'development');
if ($appEnv !== 'production') {
// ...
}
}
}
// ✅ 配置文件中使用 env()(正确位置)
// config/autoload/app.php
use function Hyperf\Support\env;
return [
'env' => env('APP_ENV', 'development'),
'debug' => env('APP_DEBUG', false),
];
```
### 3. Token 验证失败 - Secret 不一致
#### 症状
```bash
# 登录成功
TOKEN=$(curl -s -X POST http://127.0.0.1:9501/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "superadmin", "password": "Admin@2026"}' | jq -r '.data.access_token')
# 但 profile 接口返回 40102
curl -X GET "http://127.0.0.1:9501/api/auth/profile" \
-H "Authorization: Bearer $TOKEN"
# 返回: {"code": 40102, "message": "Token invalid"}
```
#### 根因
AuthService 和 JwtAuthMiddleware 使用不同的 JWT secret。
#### 解决方案
**确保两处使用相同的 secret**
```php
// AuthService.php
protected function generateToken(User $user, string $type = 'access', ?int $ttlOverride = null): string
{
$secret = 'dev-only-insecure-secret-20260304'; // 固定值,不要用 md5(__DIR__)
// ...
}
// JwtAuthMiddleware.php
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
// ...
$secret = 'dev-only-insecure-secret-20260304'; // 与 AuthService 完全相同
$decoded = JWT::decode($token, new Key($secret, 'HS256'));
// ...
}
```
**禁止的做法**
```php
// ❌ 使用 md5(__DIR__) 会导致路径不一致
$secret = 'dev-only-insecure-key-' . md5(__DIR__);
```
### 4. 服务启动失败 - 异步队列配置缺失
#### 症状
```bash
docker compose restart hyperf-skeleton
docker logs hyperf-skeleton --tail 50
```
#### 日志错误
```
[ERROR] Entry "Modules\Security\Listener\SecurityEventListener" cannot be resolved:
Entry "Hyperf\AsyncQueue\Driver\DriverFactory" cannot be resolved
```
```
[ERROR] [Error] default is a invalid driver
```
#### 根因
使用了异步队列,但缺少 `config/autoload/async_queue.php` 配置。
#### 解决方案
创建配置文件:
```php
<?php
declare(strict_types=1);
use function Hyperf\Support\env;
return [
'default' => [
'driver' => env('ASYNC_QUEUE_DRIVER', 'redis'),
'channel' => 'queue',
'retry_after' => 60,
'block_seconds' => 5,
'max_messages' => 100,
'redis' => [
'pool' => 'default',
],
],
];
```
### 5. 服务启动失败 - Config 对象未注入
#### 症状
```
[ERROR] Entry "Hyperf\Config\Config" cannot be resolved:
Parameter $configs of __construct() has no value defined or guessable
```
#### 根因
Hyperf Config 对象未在 dependencies.php 中注册。
#### 解决方案
**检查 `config/autoload/dependencies.php`**
```php
return [
Hyperf\Database\Migrations\Migrator::class => App\Database\MigratorFactory::class,
Hyperf\Database\Seeders\Seed::class => App\Database\SeedFactory::class,
// 确保 Config 不在这里注册Hyperf 会自动注册)
];
```
## 验证清单
### 新模块开发前
- [ ] 检查是否需要队列配置
- [ ] 检查是否需要缓存配置
- [ ] 检查是否需要 JWT 配置
- [ ] 所有 `env()` 都有默认值
### 代码修改后
- [ ] 不在 Service/Middleware 中使用 `config()`
- [ ] JWT secret 使用固定值或环境变量
- [ ] AuthService 和 JwtAuthMiddleware 使用相同 secret
- [ ] 重启服务并测试关键路径
### 部署前
- [ ] 生产环境设置 `JWT_SECRET` 环境变量
- [ ] 检查 .env.example 是否包含所有必要的环境变量
- [ ] 运行 Seeder 确保初始数据正确
## 调试命令
### 查看日志
```bash
# Hyperf 应用日志
docker logs hyperf-skeleton --tail 100 -f
# 数据库日志
docker logs hyperf-mysql --tail 50
# Redis 日志
docker logs hyperf-redis --tail 50
```
### 进入容器调试
```bash
docker exec -it hyperf-skeleton bash
# 运行迁移
php bin/hyperf.php migrate
# 运行 Seeder
php bin/hyperf.php db:seed
# 检查 PHP 语法
php -l modules/Auth/src/Service/AuthService.php
```
### 测试 API
```bash
# 登录
curl -X POST http://127.0.0.1:9501/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "superadmin", "password": "Admin@2026"}'
# 使用 token 访问受保护接口
TOKEN="your-access-token-here"
curl -X GET "http://127.0.0.1:9501/api/auth/profile" \
-H "Authorization: Bearer $TOKEN"