Files
order_site/docs/部署启动与数据库重建.md
T
yml2213 08318ef8d9 feat(deploy): 财务上线数据阶段编排与运维加固
- 新增 deploy/post-deploy.sh:回填 → 业务配置 → 同步 → 验收四阶段编排,支持 --only/--dry-run/窗口参数
- 新增 tools 镜像与 scripts/verify-finance-launch.sql(15 项上线验收)
- 验收口径:成本闸门二选一、要求已过账凭证、备用金支出关联、成本覆盖 WARN
- 加固:参数校验、工具镜像每次刷新、docker run 兜底超时、迁移前缀探测、验收失败退出码 2
- 工具镜像预建 /app/data,消除日志 EACCES
- 回填后与 restore-db 恢复后自动 VACUUM (ANALYZE),修复订单列表分页 COUNT 全表扫描(41 万单实测 27s → 0.17s)
- 文档:41 万单同步用 7 天窗 + 180s 超时;回填/恢复后刷新统计
2026-10-28 11:52:42 +08:00

372 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 部署启动与数据库重建
本文记录本地开发、数据库迁移、重建库和 Ubuntu 服务器部署的常用命令。
## 数据库迁移策略
项目使用 `node-pg-migrate`,迁移目录:
```text
apps/backend/src/db/migrations/
001_init.sql 空库基线(完整结构)
002_xxx.sql 后续增量(示例命名)
003_xxx.sql
...
```
记录表:`schema_migrations`。
| 场景 | 行为 |
| --- | --- |
| 全新空库 | 按序号执行 `001_init` → 全部增量 |
| 已有库升级 | 只执行尚未记录的增量文件 |
| 改历史迁移文件内容 | **不会**自动重跑;已应用文件禁止修改 |
约定:
1. `001_init.sql` 只作为基线;上线后结构变更不要改它。
2. 每次 schema 变更新增 `NNN_name.sql`(三位序号)。
3. 迁移与业务代码同一变更一起提交。
4. 后端启动会自动 `up`;也可用命令手动执行。
### 开发常用命令
```bash
# 查看本地文件与库内已应用记录
docker compose -f docker-compose.dev.yml exec -T backend npm run db:migrate:status
# 手动执行未应用迁移
docker compose -f docker-compose.dev.yml exec -T backend npm run db:migrate
# 创建下一份增量迁移模板
docker compose -f docker-compose.dev.yml exec -T backend npm run db:migrate:create -- add_column_example
```
宿主机直连(`postgres` 主机名仅在 Docker 网络内有效):
```bash
cd apps/backend
DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/order_site npm run db:migrate:status
DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/order_site npm run db:migrate
```
## 本地开发启动
首次:
```bash
cp .env.mac-docker.example .env
docker compose -f docker-compose.dev.yml up -d --build
```
日常:
```bash
docker compose -f docker-compose.dev.yml up -d
```
状态与日志:
```bash
docker compose -f docker-compose.dev.yml ps
docker compose -f docker-compose.dev.yml logs -f backend
docker compose -f docker-compose.dev.yml logs -f frontend
```
本地入口:
- 前端、后台、领取页:`http://localhost`
- 健康检查:`http://localhost/health`
- 就绪检查:`http://localhost/health/ready`
## 本地重建数据库
仅在需要清空开发数据,或本地库与迁移历史严重不一致时使用。
只重建开发库 volume:
```bash
docker compose -f docker-compose.dev.yml down
docker volume rm order_site_postgres_dev_data
docker compose -f docker-compose.dev.yml up -d --build
```
volume 名称不确定时:
```bash
docker volume ls | grep postgres_dev_data
```
删除当前 dev compose 全部 volume(会重装 node_modules volume):
```bash
docker compose -f docker-compose.dev.yml down -v
docker compose -f docker-compose.dev.yml up -d --build
```
确认迁移记录:
```bash
docker compose -f docker-compose.dev.yml exec -T backend npm run db:migrate:status
```
或:
```bash
docker compose -f docker-compose.dev.yml exec -T backend node --input-type=module -e '
const { query, closeDb } = await import("./src/db/client.ts");
try {
const result = await query("SELECT COALESCE(filename, name) AS name FROM schema_migrations ORDER BY COALESCE(filename, name)");
console.log(result.rows.map((row) => row.name).join("\n"));
} finally {
await closeDb();
}
'
```
## 本地验证
```bash
docker compose -f docker-compose.dev.yml exec -T backend npm test
docker compose -f docker-compose.dev.yml exec -T backend npm run typecheck
docker compose -f docker-compose.dev.yml exec -T backend npm run build
docker compose -f docker-compose.dev.yml exec -T frontend npm run typecheck
docker compose -f docker-compose.dev.yml exec -T frontend npm run build
```
## 服务器首次部署
建议 Ubuntu + Docker Compose。
```bash
cp .env.server.example .env
```
至少配置:
```text
APP_DOMAIN
CADDY_SITE_ADDR
WORKER_SITE_ADDR
CLAIM_BASE_URL
POSTGRES_PASSWORD
DATABASE_URL
ADMIN_SESSION_SECRET
ADMIN_DEFAULT_USERS_JSON
CORS_ALLOWED_ORIGINS
STORAGE_MODE
STORAGE_OSS_ENDPOINT
STORAGE_OSS_BUCKET
STORAGE_OSS_ACCESS_KEY_ID
STORAGE_OSS_SECRET_ACCESS_KEY
STORAGE_OSS_REGION
```
其中 `APP_DOMAIN`、`CORS_ALLOWED_ORIGINS` 与 `STORAGE_MODE=oss` / `STORAGE_OSS_*` 为生产必需:`docker-compose.yml:95` 的 `STORAGE_MODE` 生产默认即 `oss`,`apps/backend/src/config/runtime-validation.ts:107-114` 在生产环境强制校验 `STORAGE_OSS_*` 凭据并使用显式 CORS 域名白名单;`.env.server.example` 已给出这些变量的模板值,其中带 `change-me` 的占位会被 `deploy/ubuntu-deploy.sh` 的占位值检查拦住,需替换为真实值。
容器内连接 Compose PostgreSQL 时,主机名用 `postgres`:
```text
DATABASE_URL=postgres://postgres:你的数据库密码@postgres:5432/order_site
```
不要写成容器内的 `127.0.0.1`(那会指向后端容器自己)。
部署:
```bash
bash deploy/ubuntu-deploy.sh
```
脚本会:检查 Docker、检查 `.env` 占位值、检查 `DATABASE_URL`、修正 `apps/backend/data` 权限、按改动决定重建哪一端、启动、等待健康检查、打印已应用迁移与各阶段耗时。
### 按需重建(默认行为)
脚本把「上次成功部署的 commit + `.env` 指纹」记在项目根的 `.deploy-state`(已 gitignore),
再按 `git diff --name-only 上次commit..HEAD` 判断本次要重建哪一端:
| 改动 | 结果 |
| --- | --- |
| `apps/backend/**`(含 migrations) | 只重建 backend 镜像与容器 |
| `apps/frontend/**`、`deploy/caddy/**`、前端 Dockerfile | 只重建 web 镜像与容器 |
| `README.md`、`docs/**`、`deploy/*.sh`、`deploy/lib/**`、`scripts/**` | 不重建任何镜像,容器保持运行(只剩迁移 + 健康检查) |
| `docker-compose.yml`、`.env` 变化、未知路径、上次 commit 不在仓库里(force push/回滚) | 两端都重建(保守兜底) |
不需要重建的一侧既不跑 Docker 构建、也不 `--force-recreate`;如果它恰好没在运行(服务器重启过),
脚本用现有镜像把它拉起来。首次在有 `--skip-backup` 之前用本脚本时没有 `.deploy-state`,
会按"全量重建一次"处理,之后即进入增量。
备份策略同理:本次改动不含 `apps/backend/src/db/migrations/**` 时自动跳过迁移前备份
(生产上 `pg_dump` + 传 OSS 接近 12 分钟);`--backup` 可强制备份,`--skip-backup` 可无条件跳过,
只有"能算出改动范围"时才会自动跳过。
常用参数:
| 参数 | 说明 |
| --- | --- |
| `--dry-run` | 只打印计划后退出:上次部署的 commit、改动文件、要重建哪一端、是否备份、基础镜像 digest |
| `--all` | 强制两端都重建 |
| `--backend-only` / `--web-only` | 只重建指定一端 |
| `--backup` | 强制迁移前备份 |
| `--skip-backup` | 跳过迁移前备份 |
| `--reset-db` / `--yes` | 见下文「服务器重建数据库」 |
基础镜像也只补缺:`docker compose pull --ignore-buildable --policy missing`
(不再每次部署重新校验 postgres tag,避免顺手把数据库镜像静默升级)。
```bash
docker compose ps
docker compose logs -f backend
```
## 财务/成本上线后的初始化(迁移 103–105)
代码与迁移只负责建表;**存量订单回填、备用金账户、积分余额、商品目录、人工 SKU 不会自动创建**。
部署成功后按「回填 → 商品初始化 → 财务同步 → 验收」四步走:
### 1. 数据回填
```bash
bash deploy/post-deploy.sh --dry-run # 先看计划与待回填量
bash deploy/post-deploy.sh --only=backfill # ① 收入回填 + ② 履约方式回填
```
> 不要在商品目录为空时直接跑 `--only=sync`:财务同步会为每个未归一订单项写一条
> `product_mapping_missing` 成本异常(生产规模 90 天实测约 36 万条),后台异常队列会被淹没。
脚本包含 `revenue:backfill`、`fulfillment:backfill`、`finance:sync` 与只读验收四段;任何一步都能重跑,
`--only=backfill|sync|verify` 可单独执行。
> 生产 backend 镜像没有 `tsx` 与 `scripts/`,脚本通过 `tools` 镜像执行
> ([deploy/docker/tools.Dockerfile](../deploy/docker/tools.Dockerfile),`docker compose --profile tools`)。
### 2. 业务配置:后台 UI
1. 钱包管理 → 补建客服备用金账户 → 按需拨付;
2. 商品与成本 → 积分钱包 → 给客服发放三平台积分;
3. 商品与成本 → 标准商品 → 从接单模板初始化 → 按工单回填商品 → 归一人工商品;
4. 商品与成本 → 人工 SKU 配置 → 扫描并逐项配置(保存后自动回填订单项)。
> 第 3 步的商品目录/工单回填必须先于下一节 `--only=sync`。商品目录为空时同步会为每个
> 未归一订单项写一条 `product_mapping_missing` 异常(实测 90 天约 36 万条)。
### 3. 财务同步
商品归一完成后执行:
```bash
bash deploy/post-deploy.sh --only=sync # ③ 成本快照 / 销售事实
```
> 工具镜像每次运行都会重建(层缓存下只需几秒),确保 `git pull` 后执行的是当前脚本。
### 4. 总账与验收
5. 财务 → 总账与对账 → 同步销售事实到总账 → 检查试算平衡;按需关账/导入平台账单。
```bash
bash deploy/post-deploy.sh --only=verify # ④ 只读验收
```
退出码:`0` = 完成且验收无 FAIL、`1` = 执行失败、`2` = 验收未通过。
15 项检查全部 PASS 才算上线完成。若 15 号「近 7 天非人工销售事实成本覆盖」显示 WARN,
说明自动发货/接单平台仍有成本缺口,需要先归一商品并配置成本版本;WARN 不阻断验收,
但会直接影响毛利口径。
存量初始化完成后,日常重跑可直接 `bash deploy/post-deploy.sh`(回填 + 同步 + 验收一条龙)。
> 历史人工成本不补录;人工成本监控从第一次创建人工履约配置的时间开始。
> 定时任务 `cost-finance-sync` 每 5 分钟运行,但**只覆盖最近 7 天**;更早历史靠 `post-deploy.sh` 的分窗同步。
---
## 服务器日常更新
```bash
cd /home/order_site && git pull
bash deploy/ubuntu-deploy.sh # 想看这次会做什么:加 --dry-run
```
后端启动时自动执行未应用的增量迁移,一般不必单独跑迁移。
确认:
```bash
docker compose exec -T backend node --input-type=module -e '
const { query, closeDb } = await import("./dist/db/client.js");
try {
const result = await query("SELECT COALESCE(filename, name) AS name FROM schema_migrations ORDER BY COALESCE(filename, name)");
console.log(result.rows.map((row) => row.name).join("\n"));
} finally {
await closeDb();
}
'
```
## 服务器重建数据库
**会删除生产数据**。仅适合新环境、测试机或确认可丢数据的场景。
```bash
docker compose down
docker volume rm order_site_postgres_data
bash deploy/ubuntu-deploy.sh
```
或:
```bash
bash deploy/ubuntu-deploy.sh --reset-db
```
volume 名不确定时:
```bash
docker volume ls | grep postgres_data
```
不要随手:
```bash
docker compose down -v
```
这会连同 `caddy_data` / `caddy_config` 一起删掉,可能触发证书重签。
## 常见问题
### 后端一直未 ready
```bash
docker compose ps
docker compose logs --tail=120 backend
```
常见原因:
- `.env` 仍有 `change-me` 占位值
- `DATABASE_URL` 密码与已有 PostgreSQL volume 初始化密码不一致
- 容器部署里 `DATABASE_URL` 误写 `127.0.0.1`
- 某次增量迁移 SQL 失败
### 改了 `001_init.sql` 但旧库没变
已应用过的迁移不会因文件内容变更而重跑。正确做法是 **新增** `002_xxx.sql` 写增量。
仅本地可丢数据时,才用重建 volume 的方式对齐。
### 如何新增字段 / 索引
```bash
docker compose -f docker-compose.dev.yml exec -T backend \
npm run db:migrate:create -- add_orders_note
# 编辑生成的 apps/backend/src/db/migrations/00N_add_orders_note.sql
docker compose -f docker-compose.dev.yml exec -T backend npm run db:migrate
docker compose -f docker-compose.dev.yml exec -T backend npm run db:migrate:status
```
### 宿主机手动跑迁移连不上 `postgres`
`.env` 里的 `postgres` 只在 Docker 网络有效。宿主机请临时改为 `127.0.0.1` 并映射端口。