# 部署启动与数据库重建 本文记录本地开发、数据库迁移、重建库和 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` 并映射端口。