← API | 列表 | DEPLOY
提示信息
# 同伴后端 — 服务器部署指南

> 适用:PHP 8.4 · Slim 4 · PostgreSQL · Redis
> 
> 本文档描述从 git 拉取代码后,在服务器上完成部署所需的全部步骤。

---

## 目录

1. [环境要求](#1-环境要求)
2. [安装 PHP 依赖](#2-安装-php-依赖)
3. [配置环境变量 .env](#3-配置环境变量-env)
4. [初始化数据库](#4-初始化数据库)
5. [目录权限](#5-目录权限)
6. [Web 服务器配置](#6-web-服务器配置)
7. [验证部署](#7-验证部署)
8. [(可选)生成 API 文档](#8-可选生成-api-文档)
9. [生产环境加固](#9-生产环境加固)
10. [常见问题](#10-常见问题)

---

## 1. 环境要求

在开始之前,请确认服务器已安装以下软件:

| 软件 | 最低版本 | 说明 |
|------|---------|------|
| PHP | **8.4** | 需开启扩展:`pdo_pgsql`, `redis`, `mbstring`, `json`, `openssl`, `fileinfo` |
| Composer | 2.x | PHP 包管理器 |
| PostgreSQL | 14+ (推荐 18) | 主数据库 |
| Redis | 6+ | 缓存 / JWT 黑名单 / 频率限制 |
| Nginx / Apache | 任意现代版本 | Web 服务器(推荐 Nginx) |

检查 PHP 版本:
```bash
php -v
# 确认输出包含 PHP 8.4.x
```

检查必要 PHP 扩展:
```bash
php -m | grep -E "pdo_pgsql|redis|mbstring|json|openssl|fileinfo"
```

---

## 2. 安装 PHP 依赖

> **注意**:`vendor/` 目录已在 `.gitignore` 中,拉取代码后不存在,必须执行此步骤。

```bash
cd /path/to/tongban-backend

# 生产环境(不安装 dev 依赖,优化自动加载)
composer install --no-dev --optimize-autoloader

# 开发/测试环境
composer install
```

---

## 3. 配置环境变量 .env

`.env` 文件不在 git 仓库中,需手动从模板创建:

```bash
cp .env.example .env
```

然后逐项编辑 `.env`,以下是各模块的配置说明:

### 3.1 应用基础

```ini
APP_ENV=production          # 生产环境改为 production
APP_DEBUG=false             # 生产环境必须为 false,否则会暴露错误详情
APP_KEY=                    # 用于 Illuminate Encryption,必须填写
```

生成 `APP_KEY`(32字节随机串 base64 编码):
```bash
php -r "echo 'base64:' . base64_encode(random_bytes(32)) . PHP_EOL;"
```

### 3.2 数据库(PostgreSQL)

```ini
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=tongban         # 数据库名,需提前在 PostgreSQL 中创建
DB_USERNAME=postgres
DB_PASSWORD=your_password
DB_SSLMODE=require          # 生产环境改为 require
```

### 3.3 Redis

```ini
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=             # 有密码就填,无密码留空
REDIS_DB=0
REDIS_PREFIX=companion:     # 多项目共用 Redis 时建议修改前缀
```

### 3.4 JWT 鉴权

```ini
JWT_SECRET=                 # 至少 32 位随机字符串,绝对不能泄露
JWT_EXPIRE=7200             # access token 有效期(秒),默认 2 小时
JWT_REFRESH_EXPIRE=2592000  # refresh token 有效期(秒),默认 30 天
```

生成 JWT_SECRET:
```bash
php -r "echo bin2hex(random_bytes(32)) . PHP_EOL;"
```

### 3.5 阿里云 OSS(图片/音频存储)

```ini
OSS_ACCESS_KEY_ID=          # 阿里云 RAM 子账号 AccessKey
OSS_ACCESS_KEY_SECRET=
OSS_ENDPOINT=oss-cn-hangzhou.aliyuncs.com
OSS_BUCKET=tongban-signal   # 信号媒体文件 bucket
OSS_BUCKET_DEV=tongban-signal
OSS_BUCKET_AVATAR=          # 头像 bucket(通常独立)
OSS_ENDPOINT_AVATAR=oss-cn-shanghai.aliyuncs.com
OSS_DOMAIN=cdn.yourdomain.com           # 信号 CDN 域名
OSS_DOMAIN_AVATAR=avatar.yourdomain.com # 头像 CDN 域名
OSS_STS_ROLE_ARN=           # STS 授权角色 ARN,客户端直传使用
```

### 3.6 OpenIM(即时通讯)

```ini
OPENIM_API_URL=https://im-api.yourdomain.com
OPENIM_SECRET=              # 与 OpenIM 服务端配置保持一致
OPENIM_ADMIN_UID=imAdmin
OPENIM_UID_PREFIX=tb_       # IM 用户 ID 前缀
```

### 3.7 应用签名(客户端请求签名)

```ini
APP_KEY_IOS_DEV=tongban_ios_dev
APP_KEY_ANDROID_DEV=tongban_android_dev
APP_SECRET_DEV=             # 开发环境签名密钥
APP_KEY_IOS_PROD=tongban_ios_prod
APP_KEY_ANDROID_PROD=tongban_android_prod
APP_SECRET_PROD=            # 生产环境签名密钥
```

### 3.8 支付(支付宝 / 微信)

```ini
# 支付宝(App 端支付)
ALIPAY_APPID=
ALIPAY_PUBLIC_KEY=          # 支付宝平台公钥
ALIPAY_PRIVATE_KEY=         # 应用私钥

# 微信支付
WECHAT_APPID=
WECHAT_MCH_ID=
WECHAT_MCH_V3_KEY=
WECHAT_CERT_PUBLIC=         # 商户证书公钥内容
WECHAT_CERT_PRIVATE=        # 商户证书私钥内容
```

### 3.9 阿里云短信

```ini
ALIYUN_SMS_ACCESS_KEY_ID=
ALIYUN_SMS_ACCESS_KEY_SECRET=
ALIYUN_SMS_SIGN_NAME=       # 短信签名名称
ALIYUN_SMS_TEMPLATE_LOGIN=  # 登录验证码模板 ID
ALIYUN_SMS_TEMPLATE_RESET=  # 重置密码模板 ID
ALIYUN_SMS_TEMPLATE_BIND=   # 绑定手机模板 ID
```

### 3.10 其他

```ini
CDN_DOMAIN=                 # 阿里云 CDN 域名(含协议和结尾斜杠)
CDN_AUTH_SECRET=            # CDN 鉴权 key
ADMIN_TOKEN=                # 后台管理接口认证 token
ADMIN_CORS_ORIGIN=          # 后台允许的跨域来源,多个用逗号分隔
LOG_LEVEL=info              # 生产建议 info;调试时用 debug
PRIVACY_POLICY_VERSION=1    # 隐私政策版本基准
```

---

## 4. 初始化数据库

### 4.1 创建数据库

```bash
# 进入 PostgreSQL
psql -U postgres

# 创建数据库
CREATE DATABASE tongban;
\q
```

### 4.2 按顺序执行 Schema 文件

> ⚠️ **必须先执行 `00_types.sql`**,它定义了其他表所依赖的自定义枚举类型。

```bash
cd /path/to/tongban-backend

PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/00_types.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/01_users.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/02_user_devices.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/03_user_blocks.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/04_user_login_logs.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/05_user_risk_logs.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/06_user_forbidden.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/07_user_sign.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/08_user_module_cursors.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/09_user_verifications.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/10_user_cancellations.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/11_reports.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/12_signals.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/13_signal_receives.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/14_signal_replies.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/15_signal_low_quality_feedbacks.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/16_im_stranger_limit.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/17_im_daily_greet.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/18_im_day_stats.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/19_im_upload_logs.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/20_im_violation_logs.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/21_app_versions.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/22_orders.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/23_sms_logs.sql
PGPASSWORD=your_password psql -U postgres -d tongban -f database/schema/24_app_documents.sql
```

或者用一条命令批量执行(按文件名数字顺序):
```bash
ls database/schema/*.sql | sort -V | xargs -I{} sh -c \
  'echo "=== Running {} ===" && PGPASSWORD=your_password psql -U postgres -d tongban -f {}'
```

### 4.3(可选)填充基础数据

```bash
# 应用文档(隐私政策、用户协议等)
PGPASSWORD=your_password psql -U postgres -d tongban -f database/seed_documents.sql

# 应用版本数据
PGPASSWORD=your_password psql -U postgres -d tongban -f database/mock_app_versions.sql
```

---

## 5. 目录权限

确保 Web 进程(通常是 `www-data` 或 `nginx`)有读写权限:

```bash
# 创建必要目录(如果不存在)
mkdir -p logs storage

# 设置权限
chmod -R 755 /path/to/tongban-backend
chmod -R 777 logs storage

# 或者指定用户
chown -R www-data:www-data logs storage
```

---

## 6. Web 服务器配置

### Nginx 配置示例

```nginx
server {
    listen 80;
    server_name api.yourdomain.com;
    root /path/to/tongban-backend/public;
    index index.php;

    # 将所有请求转发到 index.php(Slim 4 路由)
    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;  # 根据实际路径调整
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # 禁止访问 .env 等敏感文件
    location ~ /\. {
        deny all;
    }

    # 日志
    access_log /var/log/nginx/tongban-access.log;
    error_log  /var/log/nginx/tongban-error.log;
}
```

> 生产环境加 HTTPS(Let's Encrypt / 自有证书),并在 Nginx 中开启 SSL。

### Apache 配置示例(.htaccess)

项目 `public/` 目录下需要有 `.htaccess`:

```apache
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]
```

---

## 7. 验证部署

### 7.1 检查服务是否响应

```bash
curl -X GET https://api.yourdomain.com/v1/health
# 期望返回:{"code":0,"msg":"success","data":null}
# 或 curl http://127.0.0.1/v1/health(本地测试)
```

### 7.2 检查数据库连接

```bash
php -r "
require 'vendor/autoload.php';
\$pdo = new PDO('pgsql:host=127.0.0.1;dbname=tongban', 'postgres', 'your_password');
echo 'DB OK: ' . \$pdo->query('SELECT version()')->fetchColumn() . PHP_EOL;
"
```

### 7.3 检查 Redis 连接

```bash
redis-cli ping
# 返回 PONG 则正常
```

### 7.4 访问 API 文档

浏览器打开:`https://api.yourdomain.com/docs/api`

---

## 8. (可选)生成 API 文档

```bash
# 方式一:Composer 脚本
composer run-script gen-doc
# 输出到 docs/openapi.json

# 方式二:访问接口动态刷新(需认证)
curl https://api.yourdomain.com/docs/refresh
```

---

## 9. 生产环境加固

| 项目 | 操作 |
|------|------|
| `APP_DEBUG=false` | 禁止错误详情对外暴露 |
| `APP_ENV=production` | 开启生产模式 |
| `DB_SSLMODE=require` | 强制 PostgreSQL SSL 连接 |
| `LOG_LEVEL=info` | 减少日志量,避免敏感信息泄露 |
| Nginx 屏蔽敏感路径 | 禁止访问 `.env`、`.git`、`composer.json` 等 |
| HTTPS | 全站强制 HTTPS,防止 JWT / 签名中间人攻击 |
| Redis 密码 | 设置 `requirepass`,`REDIS_PASSWORD` 同步填写 |
| 文件权限 | `logs/`、`storage/` 仅 Web 进程可写,不对外暴露 |
| `ADMIN_TOKEN` | 使用长随机串,后台接口仅限内网 IP 访问 |

---

## 10. 常见问题

### Q: `vendor/` 不存在,报 autoload 错误
**A**: 执行 `composer install --no-dev --optimize-autoloader`。

### Q: 数据库连接失败
**A**: 检查 `.env` 中 `DB_*` 配置,确认 PostgreSQL 已启动且账号有连接权限:
```bash
psql -U postgres -h 127.0.0.1 -d tongban
```

### Q: Redis 连接失败
**A**: 确认 Redis 已启动:`systemctl status redis` / `brew services list | grep redis`。

### Q: `00_types.sql` 报错"类型已存在"
**A**: 说明之前已执行过,可忽略此错误,或在 SQL 前加 `DROP TYPE IF EXISTS ... CASCADE;`。

### Q: JWT Token 校验失败
**A**: 确认 `.env` 中 `JWT_SECRET` 与客户端约定一致,且长度 ≥ 32 位。

### Q: OSS 上传失败
**A**: 检查 `OSS_ACCESS_KEY_ID` / `OSS_ACCESS_KEY_SECRET`,确认 RAM 子账号对 bucket 有 `oss:PutObject` 权限。

### Q: 生成 API 文档时 PHP 报内存溢出
**A**: 增大 PHP 内存限制:
```bash
php -d memory_limit=512M -r "..."
# 或修改 php.ini: memory_limit = 512M
```

---

## 部署检查清单

```
[ ] PHP 8.4 已安装,pdo_pgsql / redis 扩展已启用
[ ] Composer 已安装,vendor/ 已生成
[ ] .env 已从 .env.example 复制并填写完毕
[ ] APP_KEY 已生成并填写
[ ] JWT_SECRET 已生成并填写(≥32位)
[ ] PostgreSQL 数据库 tongban 已创建
[ ] database/schema/*.sql 按顺序执行完毕(00 → 24)
[ ] logs/ 和 storage/ 目录存在且有写权限
[ ] Nginx/Apache 已配置,指向 public/index.php
[ ] curl /v1/health 返回正常
[ ] API 文档页面可访问
[ ] 生产环境:APP_DEBUG=false,DB_SSLMODE=require,LOG_LEVEL=info
```