Files
video/docs/04-部署运行文档.md
T
2026-10-15 20:36:59 +08:00

8.0 KiB
Raw Blame History

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 安装依赖

cd server
python3.11 -m venv .venv
source .venv/bin/activate
pip install .            # 核心依赖
pip install ".[pay]"     # 可选:支付渠道 SDK(支付宝/微信/Tron),不接支付可跳过

2.2 准备数据库与 .env

CREATE DATABASE video CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

在 server/.env 配置环境项(不入库的连接串/密钥):

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 初始化表结构 + 首个管理员

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 启动

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/)

cd admin
npm ci
npm run build        # 产物在 admin/dist/

Nginx 参考配置(同一站点托管后台 + 反代 API):

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):

  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 的 dnsTxtHost / fallbackDomainSuffix 为你控制的域名后重新编译。

4.2 Android

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

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. 端到端验证清单

# 服务端
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 说明。