# 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 [ '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"