204 lines
8.0 KiB
Markdown
204 lines
8.0 KiB
Markdown
# 04-部署运行文档
|
||
|
||
> 本文说明如何在服务器上把项目跑起来。项目共三端:**服务端**(FastAPI,无需编译)、**管理后台**(Vue3,构建为静态文件)、**App**(Flutter,**必须编译**出 APK/IPA)。
|
||
|
||
## 1. 组成与依赖
|
||
|
||
| 端 | 目录 | 技术栈 | 运行/构建产物 |
|
||
|---|---|---|---|
|
||
| 服务端 | `server/` | Python ≥ 3.11 + FastAPI + MySQL 8 + Redis 7 | 直接运行,无需编译 |
|
||
| 管理后台 | `admin/` | Node ≥ 18 + Vue3 + Vite | `npm run build` → `admin/dist/` 静态文件 |
|
||
| App | `app/` | Flutter 3.x(Dart ≥ 3.9) | `flutter build apk` / `flutter build ipa` |
|
||
|
||
服务器需先装好:MySQL 8(utf8mb4)、Redis 7、Python 3.11+、Nginx(托管后台 + 反向代理)。
|
||
|
||
## 2. 服务端部署(server/)
|
||
|
||
### 2.1 安装依赖
|
||
|
||
```bash
|
||
cd server
|
||
python3.11 -m venv .venv
|
||
source .venv/bin/activate
|
||
pip install . # 核心依赖
|
||
pip install ".[pay]" # 可选:支付渠道 SDK(支付宝/微信/Tron),不接支付可跳过
|
||
```
|
||
|
||
### 2.2 准备数据库与 .env
|
||
|
||
```sql
|
||
CREATE DATABASE video CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||
```
|
||
|
||
在 `server/.env` 配置环境项(不入库的连接串/密钥):
|
||
|
||
```ini
|
||
APP_ENV=prod # prod 时 /docs 自动关闭
|
||
APP_HOST=0.0.0.0
|
||
APP_PORT=8000
|
||
DATABASE_URL=mysql+aiomysql://user:pass@127.0.0.1:3306/video?charset=utf8mb4
|
||
REDIS_URL=redis://127.0.0.1:6379/0
|
||
JWT_SECRET=换成足够长的随机串 # 生产必须更换
|
||
|
||
# 以下按需配置(不配置则对应功能自动降级:短信/邮件发不出去、支付走 dev_mock、USDT 不启用)
|
||
# ALIPAY_APP_ID / ALIPAY_PRIVATE_KEY / ALIPAY_PUBLIC_KEY / ALIPAY_NOTIFY_URL
|
||
# WECHAT_MCHID / WECHAT_APP_ID / WECHAT_API_V3_KEY / WECHAT_PRIVATE_KEY / WECHAT_CERT_SERIAL_NO / WECHAT_PLATFORM_CERT / WECHAT_NOTIFY_URL
|
||
# TRON_API_KEY(USDT 链上查账)
|
||
# SMS_ACCESS_KEY_ID / SMS_ACCESS_KEY_SECRET / SMS_SIGN_NAME(短信验证码)
|
||
# SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASSWORD(邮箱验证码)
|
||
```
|
||
|
||
业务参数(积分/会员/激励/提现规则)在 `server/config.yaml`,其中部分项上线后可在管理后台「系统配置」热修改。
|
||
|
||
### 2.3 初始化表结构 + 首个管理员
|
||
|
||
```bash
|
||
cd server
|
||
.venv/bin/alembic upgrade head # 建 27 张表
|
||
|
||
# 创建后台管理员(代码不自动播种,需手动执行一次)
|
||
.venv/bin/python - <<'EOF'
|
||
import asyncio
|
||
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
|
||
from app.config import settings
|
||
from app.core.security import hash_password
|
||
from app.models.admin import AdminUser
|
||
|
||
async def main():
|
||
engine = create_async_engine(settings.database_url)
|
||
async with async_sessionmaker(engine)() as s:
|
||
s.add(AdminUser(username="admin",
|
||
password_hash=hash_password("请设置强密码"),
|
||
name="超管", role="super"))
|
||
await s.commit()
|
||
await engine.dispose()
|
||
|
||
asyncio.run(main())
|
||
EOF
|
||
```
|
||
|
||
### 2.4 启动
|
||
|
||
```bash
|
||
cd server
|
||
.venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||
```
|
||
|
||
- 健康检查:`curl http://127.0.0.1:8000/health` → `{"status":"ok"}`
|
||
- **生产请单 worker 运行**(不要加 `--workers > 1`):结算周期任务(推荐激活/创作收益/超时关单)随进程内 lifespan 启动,多 worker 会重复执行。
|
||
- 生产建议用 systemd/supervisor 托管进程。
|
||
|
||
服务暴露的路径(Nginx 反代时用):
|
||
|
||
| 路径 | 说明 |
|
||
|---|---|
|
||
| `/api/v1/*` | App 全部业务接口 |
|
||
| `/api/v1/admin/*` | 管理后台接口 |
|
||
| `/r/{code}` | 推广 H5 落地页(邀请链接) |
|
||
| `/static/*` | 本地文件存储(头像/封面/视频,未接 OSS 时) |
|
||
| `/health` | 健康检查 |
|
||
|
||
## 3. 管理后台部署(admin/)
|
||
|
||
```bash
|
||
cd admin
|
||
npm ci
|
||
npm run build # 产物在 admin/dist/
|
||
```
|
||
|
||
Nginx 参考配置(同一站点托管后台 + 反代 API):
|
||
|
||
```nginx
|
||
server {
|
||
listen 443 ssl;
|
||
server_name admin.example.com;
|
||
# ssl_certificate / ssl_certificate_key ...
|
||
|
||
root /path/to/video/admin/dist;
|
||
index index.html;
|
||
|
||
location / { try_files $uri $uri/ /index.html; } # SPA 回退
|
||
location /api/ { proxy_pass http://127.0.0.1:8000; } # 后台接口
|
||
location /static/ { proxy_pass http://127.0.0.1:8000; } # 媒体文件
|
||
location /r/ { proxy_pass http://127.0.0.1:8000; } # 推广落地页
|
||
}
|
||
```
|
||
|
||
浏览器访问 `https://admin.example.com`,用 2.3 创建的账号登录。
|
||
|
||
## 4. App 编译(app/)—— 需要编译
|
||
|
||
App 是 Flutter 工程,**必须编译出安装包**分发给用户,服务器上不运行 App。
|
||
|
||
### 4.1 关键:App 如何找到服务器(不硬编码域名)
|
||
|
||
启动时按优先级确定 API Base URL([api_domain.dart](../app/lib/core/network/api_domain.dart)):
|
||
|
||
1. **`--dart-define=API_BASE` 编译期指定**(测试/自建最简单);
|
||
2. 本地缓存的上次成功域名;
|
||
3. DNS TXT 发现:解析 `txt.256kb.top` 的 TXT 记录,记录内容即 Base URL 主体;
|
||
4. 兜底域名:`md5(yyyyMM) + "_jsnbhh.vip"`(每月变化)。
|
||
|
||
> 注意:客户端业务路径**不带** `/api/v1` 前缀,因此 Base URL 必须包含前缀,例如 `https://api.example.com/api/v1`(TXT 记录内容写 `api.example.com/api/v1` 即可,客户端自动补 `https://`)。
|
||
|
||
上线二选一:
|
||
|
||
- **方式 A(推荐,免改代码)**:把 `txt.256kb.top` 的 TXT 记录指到你的 API 入口(内容:`api.example.com/api/v1`),并按第 3 节方式用 Nginx 把 `/api/v1/` 反代到 uvicorn。
|
||
- **方式 B(自己的域名体系)**:修改 [app_config.dart](../app/lib/core/config/app_config.dart) 的 `dnsTxtHost` / `fallbackDomainSuffix` 为你控制的域名后重新编译。
|
||
|
||
### 4.2 Android
|
||
|
||
```bash
|
||
cd app
|
||
flutter pub get
|
||
# 联调/内测:直连服务器
|
||
flutter build apk --release --dart-define=API_BASE=https://api.example.com/api/v1
|
||
# 正式(走 TXT 域名发现):不带 dart-define 直接构建
|
||
flutter build apk --release
|
||
```
|
||
|
||
产物:`build/app/outputs/flutter-apk/app-release.apk`,直接分发安装即可。
|
||
|
||
### 4.3 iOS
|
||
|
||
```bash
|
||
flutter build ipa --release
|
||
```
|
||
|
||
需要 macOS + Xcode + Apple 开发者证书签名(本机未装 Xcode 时可只交付 Android)。iOS 端上架前在管理后台「系统配置」确认 `ios_payment.enabled` 开关(规避苹果虚拟支付审核)。
|
||
|
||
### 4.4 支付渠道打包注意
|
||
|
||
`app/pubspec.yaml` 中 `tobias.url_scheme`、`fluwx.app_id`、两处 `universal_link` 是占位值,**正式支付上线前必须替换为商户申请的真实值**并重新编译;未接真实支付时服务端返回 `dev_mock`,App 自动走轮询查单兜底。
|
||
|
||
## 5. 上线后必改的后台配置
|
||
|
||
登录管理后台「系统配置」:
|
||
|
||
| 配置 key | 说明 |
|
||
|---|---|
|
||
| `app.invite_base_url` | 推广落地页地址,改为 `https://你的域名/r/`(影响 App 邀请链接) |
|
||
| `app.version.android` / `app.version.ios` | JSON:`latest_version` / `latest_version_code` / `min_version_code` / `update_url` / `notice`,控制版本检查与强制更新 |
|
||
| `ios_payment.enabled` | iOS 支付开关(审核期可关闭) |
|
||
| 敏感词、轮播公告等 | 按需 |
|
||
|
||
## 6. 端到端验证清单
|
||
|
||
```bash
|
||
# 服务端
|
||
curl http://127.0.0.1:8000/health # ok
|
||
curl "http://127.0.0.1:8000/api/v1/videos/feed?type=hot&limit=1" # code:0
|
||
curl "http://127.0.0.1:8000/api/v1/search/hot" # code:0
|
||
|
||
# 管理后台:浏览器打开 → 登录 → 视频审核/用户列表有数据
|
||
|
||
# App:安装 APK → 启动页过版本检查 → Feed 出视频 → 注册/登录 → 观看计费
|
||
```
|
||
|
||
## 7. 已知限制(当前版本)
|
||
|
||
- **媒体存储为本地文件**(`server/uploads`,经 `/static` 访问),接口返回相对路径;生产接入 OSS/CDN 使服务端返回绝对 URL 需二次开发(`storage_service` 已按此边界隔离)。
|
||
- 短信/邮件/USDT/支付宝/微信未配置密钥时自动降级(验证码发不出、支付走 dev_mock),不影响其余功能。
|
||
- CORS 当前为 `*`,生产可在 `app/main.py` 按需收紧。
|
||
- 结算周期任务在 API 进程内运行,见 2.4 单 worker 说明。
|