初始化

This commit is contained in:
2026-03-05 21:27:11 +08:00
commit 130de0fd5d
140 changed files with 21972 additions and 0 deletions

View File

@@ -0,0 +1,304 @@
# 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"

226
docs/runbooks/deployment.md Normal file
View File

@@ -0,0 +1,226 @@
# 🚢 Deployment Runbook
> PHP Hyperf + Swoole + Vue 3 部署流程指南
---
## 环境
| 环境 | URL | 触发 |
|------|-----|------|
| Development | `localhost:8200` (前端) / `localhost:9501` (后端) | 本地开发 |
| Staging | `staging.example.com` | merge to `develop` |
| Production | `www.example.com` | merge to `main` |
## Docker Compose 模板
```yaml
version: '3.8'
services:
# Nginx — 负载均衡 + 静态资源 + SSL
nginx:
image: nginx:1.25-alpine
container_name: app_nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
- ./docker/nginx/conf.d:/etc/nginx/conf.d
- ./Case-Database-Frontend-user/dist:/usr/share/nginx/html/user
- ./Case-Database-Frontend-admin/dist:/usr/share/nginx/html/admin
- ./docker/nginx/ssl:/etc/nginx/ssl
depends_on:
- hyperf
networks:
- app_network
restart: unless-stopped
# Hyperf — PHP 8.1 + Swoole (HTTP + WebSocket)
hyperf:
build:
context: ./Case-Database-Backend
dockerfile: Dockerfile
container_name: app_backend
ports:
- "9501:9501" # HTTP API
- "9502:9502" # WebSocket
volumes:
- ./Case-Database-Backend:/opt/www
environment:
- APP_ENV=${APP_ENV:-production}
- DB_HOST=mysql
- DB_PORT=3306
- DB_DATABASE=${DB_DATABASE}
- DB_USERNAME=${DB_USERNAME}
- DB_PASSWORD=${DB_PASSWORD}
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_AUTH=${REDIS_AUTH}
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
networks:
- app_network
restart: unless-stopped
# MySQL 8.1 — 主数据库
mysql:
image: mysql:8.1
container_name: app_mysql
ports:
- "${DB_PORT:-3307}:3306"
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: ${DB_DATABASE}
MYSQL_USER: ${DB_USERNAME}
MYSQL_PASSWORD: ${DB_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
- ./docker/mysql/conf.d:/etc/mysql/conf.d
- ./docker/mysql/init:/docker-entrypoint-initdb.d
command: >
--default-authentication-plugin=caching_sha2_password
--character-set-server=utf8mb4
--collation-server=utf8mb4_unicode_ci
--max-connections=500
--innodb-buffer-pool-size=1G
--slow-query-log=ON
--long-query-time=1
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
networks:
- app_network
restart: unless-stopped
# Redis 7 — 缓存 + 队列 + Session
redis:
image: redis:7.2-alpine
container_name: app_redis
ports:
- "${REDIS_PORT:-6379}:6379"
command: redis-server --requirepass ${REDIS_AUTH} --maxmemory 512mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_AUTH}", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- app_network
restart: unless-stopped
volumes:
mysql_data:
driver: local
redis_data:
driver: local
networks:
app_network:
driver: bridge
```
## Backend Dockerfile
```dockerfile
FROM hyperf/hyperf:8.1-alpine-v3.18-swoole
LABEL maintainer="Enterprise Digital Platform"
ENV TIMEZONE=Asia/Shanghai
ENV APP_ENV=production
WORKDIR /opt/www
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-scripts
COPY . .
RUN composer dump-autoload -o
EXPOSE 9501 9502
ENTRYPOINT ["php", "/opt/www/bin/hyperf.php", "start"]
```
## 部署检查清单
### 部署前
- [ ] 所有后端测试通过 (`composer test`)
- [ ] 所有前端测试通过 (`npm test`)
- [ ] 代码审查已批准
- [ ] 数据库迁移已准备 (`php bin/hyperf.php migrate:status`)
- [ ] 环境变量已配置 (`.env`)
- [ ] Redis 连接正常
- [ ] 静态分析通过 (`composer analyse`)
### 部署步骤
```bash
# 1. 拉取最新代码
git pull origin main
# 2. 后端依赖更新
cd Case-Database-Backend && composer install --no-dev -o
# 3. 数据库迁移
php bin/hyperf.php migrate
# 4. 前端构建
cd Case-Database-Frontend-user && npm ci && npm run build
cd ../Case-Database-Frontend-admin && npm ci && npm run build
# 5. 重启服务
docker-compose restart hyperf
docker-compose restart nginx
# 6. 验证
curl -s https://your-domain.com/admin/health | jq
```
### 部署后
- [ ] 健康检查通过
- [ ] 监控无异常 (CPU/内存/连接数)
- [ ] 数据库迁移成功
- [ ] WebSocket 连接正常
- [ ] 队列消费进程运行中
## 回滚
```bash
# 代码回滚
git revert HEAD
git push origin main
# 数据库回滚
php bin/hyperf.php migrate:rollback --step=1
# Docker 容器回滚到上一个镜像
docker-compose pull hyperf
docker-compose up -d hyperf
```
## 扩展到多实例
```bash
# 水平扩展 Hyperf 实例
docker-compose up -d --scale hyperf=3
# Nginx upstream 配置多后端
upstream hyperf_cluster {
server hyperf_1:9501;
server hyperf_2:9501;
server hyperf_3:9501;
}
```
---
*最后更新: YYYY-MM-DD*

View File

@@ -0,0 +1,54 @@
# 🚨 Incident Response
> 事故响应流程 (PHP Hyperf + Vue 3)
---
## 严重等级
| 等级 | 定义 | 响应时间 |
|------|------|----------|
| P0 | 服务完全不可用 | 15 分钟内 |
| P1 | 核心功能受损 | 1 小时内 |
| P2 | 非核心功能受损 | 4 小时内 |
| P3 | 轻微问题 | 1 工作日 |
## 响应流程
1. **确认**: 确认问题、评估影响范围和严重等级
2. **通知**: 通知相关人员 (企业微信 / Slack #incidents)
3. **修复**: 定位问题、实施修复或回滚
4. **验证**: 确认修复有效
5. **复盘**: 编写事后分析报告
## 常用命令
```bash
# 查看后端日志
tail -f runtime/logs/hyperf.log
# Docker 查看日志
docker compose logs -f hyperf
# 检查数据库连接
php bin/hyperf.php tinker --execute="Db::select('SELECT 1')"
# 检查 Swoole 状态
curl http://localhost:9501/admin/health
# 快速回滚代码
git revert HEAD && git push origin main
# 回滚数据库迁移
php bin/hyperf.php migrate:rollback --step=1
# Docker 回滚到上一版本
docker compose pull && docker compose up -d
# 重启服务
docker compose restart hyperf
```
---
*最后更新: 2026-02-24*