35bfebd3e3
- 9b0d0c133035: wechat_account.list_order 进迁移链(此前仅模型声明, 干净库 /api/accounts 即 500);sa.inspect 幂等守卫兼容已手动加列的 smoke.db - fe63c6789e94(验证中发现的预存缺口,超出原清单): Task 14 的 5 列模型 类型修正(4×URL String(1024)→Text, db_size Integer→BigInteger)从未落 链——纯 alembic 新库会复活 >1KB 链接截断;对齐后干净库 alembic check 全绿。SQLite message 表 batch 重建会掉 FTS 触发器+rowid 脱节(docstring 记明 ensure_fts+rebuild_fts 修复,smoke.db 已修复验证) - scrape_via_page: _iter_moments_pages 每页一行进度日志(镜像 _fetch_pages) - messages: MsgTypeParam=Literal[tuple(_MSG_TYPE_FILTER)] 派生,杜绝 双处罗列漂移(openapi enum 验证一致,492 绿含 422 行为) - 文档: ChatGroup 复合PK docstring/groups room_id 多行表述/测试名去 单行假设;Dockerfile 补 alembic.ini+migrations;DEPLOY 增「数据库 迁移」节(新库/旧库stamp路径/回填脚本显式DB_URL)+运维表升级行 Co-Authored-By: Claude <noreply@anthropic.com>
173 lines
6.7 KiB
Markdown
173 lines
6.7 KiB
Markdown
# 云客数据镜像 — 服务器部署指南
|
||
|
||
> 2026-08-21|适用于 Linux 服务器 + Docker Compose 五服务部署
|
||
|
||
## 架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────┐
|
||
│ Docker Compose │
|
||
│ ┌────────┐ ┌────────┐ ┌────────────────────┐ │
|
||
│ │ MySQL 8│ │ Redis 7│ │ backend (FastAPI) │ │
|
||
│ │ :3306 │ │ :6379 │ │ :8000 │ │
|
||
│ └────┬───┘ └────┬───┘ └─────────▲──────────┘ │
|
||
│ │ │ │ /api │
|
||
│ ┌────▼───────────▼───┐ ┌────────┴──────────┐ │
|
||
│ │ scheduler │ │ frontend (nginx) │ │
|
||
│ │ APScheduler │ │ :5173 → :80 │ │
|
||
│ │ + Playwright │ └───────────────────┘ │
|
||
│ │ + Chromium │ │
|
||
│ └────────────────────┘ │
|
||
└─────────────────────────────────────────────────┘
|
||
```
|
||
|
||
- **scheduler**:每晚 02:00 全量 + 每 4h 消息增量 + 每周日 05:00 全量对齐(全部浏览器 fetch 零风控)→ 需要 Playwright + Chromium(镜像已内置)
|
||
- **restart: unless-stopped**:容器崩溃自动重启(服务器上的守护方案,无需本地计划任务)
|
||
|
||
## 前置要求
|
||
|
||
- Linux 服务器(2C4G 起步,磁盘 ≥ 20GB:MySQL 数据 + 媒体文件 2.5GB+)
|
||
- Docker 24+ 与 Docker Compose v2
|
||
- 一个有效的云客会话 cookie(从浏览器登录云客后复制)
|
||
|
||
## 部署步骤
|
||
|
||
### 1. 上传代码
|
||
|
||
```bash
|
||
git clone <你的仓库地址> yunke
|
||
cd yunke
|
||
```
|
||
|
||
### 2. 配置 .env
|
||
|
||
```bash
|
||
cp yunke-server/.env.example .env # 或手动创建
|
||
```
|
||
|
||
必须设置的项:
|
||
|
||
```ini
|
||
# 云客会话(从浏览器 F12 → Network → 任意请求的 Cookie 头复制)
|
||
YK_USERID=<32位userId>
|
||
YK_COMPANY=<公司码>
|
||
YK_COOKIE=user=xxx; userToken=yyy
|
||
|
||
# MySQL(docker-compose 自动创建库和用户)
|
||
MYSQL_ROOT_PASSWORD=<强密码>
|
||
MYSQL_PASSWORD=<强密码>
|
||
|
||
# 查看后台的访问令牌(留空=无鉴权,仅限内网)
|
||
API_TOKEN=<随机长字符串>
|
||
```
|
||
|
||
### 3. 启动
|
||
|
||
```bash
|
||
docker compose --env-file .env up -d --build
|
||
```
|
||
|
||
首次构建约 5-10 分钟(含 Chromium 下载 ~150MB)。启动后:
|
||
|
||
```bash
|
||
docker compose ps # 五服务全部 Up(healthy)
|
||
docker compose logs -f scheduler # 看调度器日志
|
||
```
|
||
|
||
### 4. 数据库迁移(alembic schema)
|
||
|
||
后端启动**不会**自动建表,schema 由 alembic 迁移链管理(镜像已打包
|
||
`alembic.ini` + `migrations/`,`DB_URL` 由 compose 注入)。
|
||
|
||
**新库首次部署**(空 MySQL 直接建全量表):
|
||
|
||
```bash
|
||
docker compose exec backend alembic upgrade head
|
||
```
|
||
|
||
**旧库升级**(2026-08-22 `migrate_sqlite_to_mysql.py` 搬迁产物,没有
|
||
alembic_version 表)——2026-08-25 实战踩通的完整路径,原样记录:
|
||
|
||
```bash
|
||
# 1) 前置校验:chat_group 无 NULL owner_account_id(复合 PK 迁移要求 NOT NULL,
|
||
# 非 0 则是必须人工核对的数据问题,不要带病升级)
|
||
docker compose exec mysql mysql -uyunke -p<密码> yunke \
|
||
-e "SELECT COUNT(*) FROM chat_group WHERE owner_account_id IS NULL"
|
||
|
||
# 2) stamp 到搬迁脚本对应的链上位置,再升级到 head
|
||
docker compose exec backend alembic stamp d8eee96cd017
|
||
docker compose exec backend alembic upgrade head
|
||
```
|
||
|
||
**每次拉新代码后**:
|
||
|
||
```bash
|
||
docker compose exec backend alembic upgrade head
|
||
```
|
||
|
||
涉及存量数据回填的更新另跑回填脚本(幂等,重跑 0 改动;脚本默认连
|
||
`./smoke.db`,服务器上须显式传 DB_URL——容器 env 已带,直接透传):
|
||
|
||
```bash
|
||
docker compose exec backend python -c "import asyncio,os; from scripts.backfill_chat_group_fields import main; asyncio.run(main(os.environ['DB_URL']))"
|
||
docker compose exec backend python -c "import asyncio,os; from scripts.backfill_msg_types import main; asyncio.run(main(os.environ['DB_URL']))"
|
||
```
|
||
|
||
### 5. 验证
|
||
|
||
```bash
|
||
curl http://localhost:8000/api/health # {"status":"ok"}
|
||
curl http://localhost:5173/ # 前端页面
|
||
```
|
||
|
||
### 6. 数据迁移(把本地 SQLite 数据搬到服务器 MySQL)
|
||
|
||
**方式 A:重抓(推荐,数据最新)**
|
||
|
||
服务器上手动跑一轮全量:
|
||
|
||
```bash
|
||
docker compose exec scheduler bash
|
||
DB_URL="mysql+asyncmy://yunke:<密码>@mysql:3306/yunke" \
|
||
bash scripts/full_sync_all.sh
|
||
```
|
||
|
||
耗时 6-12 小时(消息全量翻页),跑完后调度器自动接管日常增量。
|
||
|
||
**方式 B:SQLite → MySQL 数据搬迁**
|
||
|
||
把本地 smoke.db 和 media/ 目录传到服务器,用 Python 脚本逐表导:
|
||
(如有需要我可以写迁移脚本,~100 行)
|
||
|
||
### 7. 媒体文件
|
||
|
||
媒体存在 Docker volume `media_data` 里。本地已有 2.5GB 媒体的话:
|
||
|
||
```bash
|
||
# 本地打包
|
||
tar czf media.tar.gz -C yunke-server media/
|
||
|
||
# 传到服务器后灌入 volume
|
||
docker run --rm -v yunke_media_data:/target -v $(pwd):/src alpine \
|
||
sh -c "cd /target && tar xzf /src/media.tar.gz --strip-components=1"
|
||
```
|
||
|
||
## 日常运维
|
||
|
||
| 操作 | 命令 |
|
||
|---|---|
|
||
| 看调度日志 | `docker compose logs -f scheduler` |
|
||
| 手动触发全量 | `docker compose exec scheduler bash scripts/full_sync_all.sh` |
|
||
| 换 cookie | 改 `.env` 的 `YK_COOKIE` → `docker compose restart scheduler backend` |
|
||
| 备份 MySQL | `docker compose exec mysql mysqldump -uroot -p<密码> yunke > backup.sql` |
|
||
| 备份媒体 | `docker run --rm -v yunke_media_data:/data -v $(pwd):/out alpine tar czf /out/media.tgz -C /data .` |
|
||
| 升级版本 | `git pull && docker compose up -d --build && docker compose exec backend alembic upgrade head`(回填见「数据库迁移」) |
|
||
|
||
## 已知注意事项
|
||
|
||
1. **cookie 过期**:云客会话定期失效,过期后日志出现 `⚠️ cookie 可能已过期` 提示 → 从浏览器重新复制 `YK_COOKIE` 并 restart
|
||
2. **首次消息全量很慢**(6-12h),之后增量每轮 15-30 分钟
|
||
3. **FTS 搜索**:MySQL 上自动回退 LIKE(暂无全文索引);如搜索量大可在 MySQL 建 FULLTEXT 索引
|
||
4. **时区**:容器默认 UTC;调度按容器时区触发(FULL_SYNC_HOUR=2 即 UTC 02:00)。要北京时间可在 compose 加 `TZ: Asia/Shanghai`
|
||
5. **服务器需能访问 crm2.yunkecn.com 和 *.oss.yunkecn.com**(抓取 + 媒体下载)
|