From 1df1b9520b70318a3461fbb280a27978d72e2732 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sun, 8 Nov 2026 21:30:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=87=E6=A1=A3=EF=BC=9A=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E7=94=9F=E4=BA=A7=E5=8D=87=E7=BA=A7=E6=93=8D=E4=BD=9C=E6=89=8B?= =?UTF-8?q?=E5=86=8C=E5=B9=B6=E5=90=8C=E6=AD=A5=E5=9F=BA=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 4 +- docs/README.md | 1 + docs/数据库备份与恢复.md | 4 + docs/生产升级操作手册.md | 657 ++++++++++++++++++++++++++ docs/统一订单履约与财务模型-权威版.md | 31 +- docs/部署启动与数据库重建.md | 8 +- 6 files changed, 689 insertions(+), 16 deletions(-) create mode 100644 docs/生产升级操作手册.md diff --git a/AGENTS.md b/AGENTS.md index 65b7039c..38aa0f8e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,7 +12,9 @@ 3. 涉及订单、内部 SKU、履约、商品、成本或财务时,必须完整阅读 [docs/统一订单履约与财务模型-权威版.md](docs/统一订单履约与财务模型-权威版.md)。 4. 涉及部署、数据库或备份时,再读取对应的运维文档。 -5. `docs/archive/` 只用于追溯,不作为实现依据。 +5. 涉及生产升级、数据库迁移、领域模型切换或财务重建时,必须完整阅读 + [docs/生产升级操作手册.md](docs/生产升级操作手册.md)。 +6. `docs/archive/` 只用于追溯,不作为实现依据。 ## 唯一权威 diff --git a/docs/README.md b/docs/README.md index b925e268..2bd3183d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,6 +18,7 @@ | 文档 | 用途 | | --- | --- | +| [生产升级操作手册](生产升级操作手册.md) | 含迁移、履约切换、财务重建的生产升级与回滚步骤 | | [部署启动与数据库重建](部署启动与数据库重建.md) | 本地与生产部署、数据库重建 | | [数据库备份与恢复](数据库备份与恢复.md) | 备份、恢复、OSS | | [数据库SQL约束与运维](数据库SQL约束与运维.md) | SQL 护栏与生产观测 | diff --git a/docs/数据库备份与恢复.md b/docs/数据库备份与恢复.md index 01f21ed7..5a977318 100644 --- a/docs/数据库备份与恢复.md +++ b/docs/数据库备份与恢复.md @@ -68,6 +68,10 @@ bash deploy/restore-db.sh --verify `--verify` 会把最新带有效清单的备份恢复到一个隔离的临时 PostgreSQL 容器(不接入业务网络,不影响线上库),并打印迁移列表与各表行数。建议之后每月执行一次演练——没验证过的备份等于没有备份。 +生产升级前的恢复验证是硬门禁,并且必须验证本次生成的指定备份,而不是只验证任意旧备份。 +具体步骤和当前恢复阻断项见 +[生产升级操作手册](生产升级操作手册.md)。恢复验证未通过时禁止执行生产迁移。 + ### 4. 配置每日定时备份 ```bash diff --git a/docs/生产升级操作手册.md b/docs/生产升级操作手册.md new file mode 100644 index 00000000..23a9368a --- /dev/null +++ b/docs/生产升级操作手册.md @@ -0,0 +1,657 @@ +--- +type: 生产运维操作手册 +description: order_site 生产结构升级、数据切换、验收与回滚的唯一执行步骤。 +tags: [生产, 部署, 数据库, 迁移, 备份, 回滚, 财务] +status: current +version: 1.0 +updated_at: 2026-11-08 +--- + +# 生产升级操作手册 + +> 本手册适用于包含数据库迁移、领域模型切换或财务重建的生产升级。 +> 业务与架构口径仍以 +> [统一订单履约与财务模型-权威版](统一订单履约与财务模型-权威版.md) 为唯一权威。 +> +> 涉及普通文档修改、无迁移的小版本更新,可继续使用 +> [部署启动与数据库重建](部署启动与数据库重建.md)。 +> 只要包含迁移、表结构切换或派生数据重建,必须按本手册执行。 + +## 一、本次升级基线 + +以下信息是 2026-11-08 的首次执行基线。后续每次升级必须由发布负责人更新, +旧值只允许保留在发布记录中,不能继续作为执行依据。 + +| 项目 | 值 | +| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 发布日期 | 2026-11-08 | +| 发布分支 | `dev_11.8` | +| 目标提交 | `d76e8265f806342c670d570e8ee72f5e8ab08fdc` | +| 生产起始迁移 | `146` | +| 目标迁移 | `150` | +| 新增迁移 | `147_internal_sku_product_routing.sql`、`148_fulfillment_execution_truth.sql`、`149_finance_derived_scope.sql`、`150_voucher_consume_order_received_scope.sql` | +| 升级类型 | 履约、内部 SKU、商品路由、财务派生数据硬切 | +| 预计维护窗口 | 2–3 小时 | +| 核心停写时间 | 20–40 分钟 | + +同生产规模本地演练结果: + +- 数据量:订单 `456777`、订单项 `456762`、履约 `456762`。 +- `146 → 150` 单事务迁移:约 `61 秒`。 +- 90 天财务重建:约 `14–17 秒`。 +- 第二次财务重建:`ledger.adjusted = 0`,幂等成立。 +- 严格财务审计:`critical: []`。 +- 原始订单、订单项、收入、退款和资金流水守恒。 + +## 二、绝对禁止 + +1. 禁止使用 `deploy/ubuntu-deploy.sh` 执行本次 `147–150` 升级;它会在旧后端 + 仍运行时执行迁移。 +2. 禁止在生产执行 `deploy/ubuntu-deploy.sh --reset-db`。 +3. 禁止使用 `--skip-backup`。 +4. 禁止在迁移前跳过 `restore-db.sh --verify`。 +5. 禁止在旧后端仍运行时执行 `147–150` 硬切迁移。 +6. 禁止迁移后用旧镜像启动后端;旧代码不兼容 `order_items.legacy_*`。 +7. 禁止为了赶进度绕过硬门禁、数据守恒检查或财务审计。 +8. 禁止自动猜测缺失渠道、商品归属或人工配置。 +9. 禁止删除或重写原始订单、退款、真实工单、资金流水和审计日志。 +10. 禁止在迁移过程中重启 PostgreSQL、强制杀迁移事务或重复启动第二个迁移进程。 +11. 禁止在本轮观察期结束前删除 `legacy_*` 字段和兼容表。 + +## 三、角色与记录 + +执行前至少确定以下角色: + +| 角色 | 职责 | +| ------------ | ---------------------------------------------- | +| 发布负责人 | 冻结提交、确认窗口、决定继续或回滚 | +| 数据库执行人 | 备份、迁移、统计刷新、数据检查 | +| 验收人 | 独立执行审计、核对输出,不复用执行人的口头结论 | +| 业务负责人 | 确认已知待分类项、财务口径和恢复营业 | + +整个过程中必须记录: + +- 升级前后 commit。 +- 升级前后迁移号。 +- 数据库与配置备份 OSS key、sha256、迁移号和文件大小。 +- 每个阶段的开始时间、结束时间和退出码。 +- 三组审计的完整输出。 +- 财务重建 dry-run 与 execute 的完整 JSON。 +- 上线验收 SQL 的 PASS、FAIL、WARN 清单。 +- 继续、暂停或回滚的决定及确认人。 + +## 四、上线前门禁 + +以下条件必须全部满足,否则不得进入维护窗口。 + +### 4.1 代码门禁 + +```bash +cd /srv/order_site # 替换为服务器实际目录 +git fetch --prune +git switch dev_11.8 +git pull --ff-only +git status --short +git rev-parse HEAD +``` + +要求: + +- `git status --short` 无输出。 +- `git rev-parse HEAD` 等于本次批准的提交。 +- 后端 `npm run check` 通过。 +- 前端 `npm run check` 通过。 +- 本次发布没有未提交文件。 + +### 4.2 生产只读基线 + +先执行只读检查,不写数据库: + +```bash +docker compose exec -T postgres \ + sh -lc 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -v ON_ERROR_STOP=1 -f -' <<'SQL' +BEGIN READ ONLY; + +SELECT COALESCE(filename, name) AS migration +FROM schema_migrations +ORDER BY COALESCE(filename, name) DESC +LIMIT 5; + +SELECT + (SELECT count(*) FROM orders) AS orders, + (SELECT count(*) FROM order_items) AS order_items, + (SELECT count(*) FROM order_refunds WHERE status = 'confirmed') AS refunds, + (SELECT COALESCE(sum(amount_fen), 0) FROM order_refunds WHERE status = 'confirmed') AS refund_fen, + (SELECT count(*) FROM work_orders) AS work_orders, + (SELECT count(*) FROM fulfillment_tasks) AS fulfillment_tasks, + (SELECT count(*) FROM fund_entries) AS fund_entries; + +COMMIT; +SQL +``` + +要求: + +- 当前最高迁移必须是 `146`。 +- 订单、订单项、退款和资金流水数量记录到发布单。 +- `docker compose ps`、`/health/ready` 和外网主入口均正常。 + +### 4.3 恢复验证硬门禁 + +这是 P0 门禁。备份文件存在不等于可回滚。 + +```bash +bash deploy/backup-db.sh +bash deploy/restore-db.sh --list +bash deploy/restore-db.sh --verify --key '<本次备份完整 OSS key>' +``` + +必须确认: + +- `backup-db.sh` 退出码为 `0`。 +- OSS manifest、sha256 和文件大小有效。 +- `restore-db.sh --verify` 退出码为 `0`。 +- 临时恢复库最高迁移为升级前迁移。 +- 订单、订单项等关键表数量与生产只读基线一致。 + +> 2026-11-08 审核结论:当前备份恢复到隔离库时,会因 +> `idx_order_items_pending_internal_sku` 依赖函数恢复顺序,以及 `livehub_ro` +> 角色不存在而返回非零。恢复脚本因此会判定备份不可用。 +> +> 在上述问题修复并重新验证通过前,本次升级状态为 **禁止上线**。 +> 不允许使用 `--allow-unverified`、手工忽略 `pg_restore` 错误或跳过此门禁。 + +### 4.4 调度任务门禁 + +升级前在后台关闭全部定时任务: + +```text +后台 → 平台配置 → 监控任务 → 任务总开关 → 关闭 → 保存任务 +``` + +要求: + +- 总开关显示关闭。 +- 所有任务不再排下一次执行。 +- 升级期间不得重新开启,直到财务重建、业务配置和上线验收全部通过。 + +该配置保存在数据库中,会被升级前备份一并保存。 + +## 五、维护窗口执行顺序 + +严格按照以下顺序执行。任何一步失败都立即停止,不得跳步。 + +### 5.1 预构建镜像 + +在停止业务前完成构建,不替换正在运行的容器: + +```bash +docker compose build backend web +docker compose --profile tools build tools +``` + +构建失败时停止,不进入维护窗口。 + +### 5.2 进入维护状态 + +先把公网流量切到维护页,或只允许办公网 IP 访问,并保留管理员内部访问通道。 +迁移后的业务配置需要通过后台 UI 完成,不能全程只启动 backend。 +如果无法在维护期间隔离公网用户和保留管理员入口,暂停升级。 + +通知停止接单后执行: + +```bash +docker compose stop web backend +docker compose ps +``` + +要求: + +- `web`、`backend` 已停止。 +- PostgreSQL 保持运行。 +- 此时开始计算核心停写时间。 + +### 5.3 生成最终升级前备份 + +虽然 4.3 已验证恢复链路,这里仍必须生成停机后的最终备份: + +```bash +# 重新执行 4.2 的只读计数 SQL,记录停机后的最终基线 +bash deploy/backup-db.sh +bash deploy/restore-db.sh --list +``` + +记录: + +- 完整 OSS key。 +- 对应配置备份 OSS key。 +- manifest 中的迁移号,必须为 `146`。 +- sha256 和文件大小。 + +最终停机备份也必须做隔离恢复验证: + +```bash +bash deploy/restore-db.sh --verify --key '<最终备份完整 OSS key>' +``` + +要求: + +- 命令退出码为 `0`。 +- manifest、sha256 和下载校验通过。 +- 临时恢复库最高迁移为 `146`。 +- 订单、订单项和资金流水数与停机后的最终基线一致。 + +最终备份恢复验证未通过时,不得执行迁移。 + +### 5.4 执行数据库迁移 + +必须先确认旧后端已停止,再执行: + +```bash +docker compose run --rm --no-deps backend node dist/db/migrate.js +``` + +预期执行: + +```text +147_internal_sku_product_routing +148_fulfillment_execution_truth +149_finance_derived_scope +150_voucher_consume_order_received_scope +``` + +要求: + +- 命令退出码为 `0`。 +- 不允许中断、重复启动或并行启动。 +- 迁移失败时保持后端停止,不自动重启。 + +迁移后检查: + +```bash +docker compose exec -T postgres sh -lc \ + 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc " + SELECT COALESCE(filename, name) + FROM schema_migrations + ORDER BY COALESCE(filename, name) DESC + LIMIT 5 + "' +``` + +最高迁移必须为 `150`。 + +### 5.5 刷新统计信息 + +迁移后立即执行,否则履约审计可能超时并产生错误计划: + +```bash +docker compose exec -T postgres sh -lc \ + 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -v ON_ERROR_STOP=1 -c " + VACUUM (ANALYZE) + orders, + order_items, + fulfillments, + fulfillment_attempts, + fulfillment_issues, + internal_skus, + sku_channel_routes, + manual_fulfillment_configs, + product_catalog, + cost_snapshots, + sales_daily_facts, + finance_journal_entries + "' +``` + +命令失败时停止上线;不要用放宽超时替代统计刷新。 + +### 5.6 启动新后端 + +先只启动后端,不启动 Web: + +```bash +docker compose up -d --no-deps --force-recreate backend +docker compose exec -T backend sh -lc \ + 'curl -fsS http://127.0.0.1:3000/health/ready' +``` + +要求: + +- 健康检查返回成功。 +- 后端日志中定时任务系统显示已停用。 +- 不允许用旧镜像执行此步骤。 + +## 六、迁移后审计 + +### 6.1 数据回填与订单级核销补投 + +调度任务保持关闭,执行: + +```bash +bash deploy/post-deploy.sh --only=backfill +``` + +要求: + +- 命令退出码为 `0`。 +- `revenue:backfill` 完成后 `revenue_basis = 'unknown'` 为 `0`。 +- `voucher:backfill-order-received` 完成最近 7 天订单级核销补投,剩余待入队为 `0`。 +- 新任务来源为 `order_received`,`target_key` 使用 `order:<订单ID>`。 +- `VACUUM ANALYZE` 成功。 + +`post-deploy.sh` 末尾仍可能打印旧上线手册链接和“从接单模板初始化”提示; +以本手册为准,不要按旧提示重复初始化商品。 + +### 6.2 商品路由审计 + +```bash +docker compose --profile tools run --rm --no-deps tools \ + npm run audit:product-routing -- --strict +``` + +以下结构性异常必须为 `0`: + +- 无主路由且无人工配置的 `man:` 商品。 +- 同一内部 SKU 多主路由。 +- `manual / third_party_voucher` 商品缺少内部 SKU。 +- 非法商品来源。 + +`fallback_without_primary`、`missing_manual_route` 和未路由商品可以保留为 +“待分类”,但必须逐类记录数量,交业务负责人在本轮维护窗口内确认。 +严格审计可能因这些已知待分类项返回非零;只有确认失败项全部属于已签字类别、 +且上述结构性异常均为 `0` 时,才允许继续。 + +### 6.3 履约审计 + +```bash +docker compose --profile tools run --rm --no-deps tools \ + npm run audit:fulfillment -- --strict +``` + +以下完整性指标必须为 `0`: + +- 正式履约缺失。 +- 正式履约状态差异。 +- 执行尝试缺失。 +- 开放异常缺失。 +- 履约事件未关联正式记录。 +- 近 24 小时重复尝试。 + +以下业务异常不得增加,并必须已经进入结构化待办: + +- `missing_internal_sku` +- `missing_channel` +- `manual_config_missing` +- `carrier_missing` +- `state_inconsistent` +- `user_refund` +- `platform_cancelled` + +其中 `state_inconsistent`、售后关闭仍进入履约队列、已分配但无有效载体属于 +恢复营业前必须处理或由业务负责人明确签字的阻塞项。 + +2026-11-08 演练基线: + +- 待处理履约项 `476`。 +- 状态不一致 `7`。 +- 售后关闭仍进入履约队列 `24`。 +- 已分配但无有效载体 `27`。 + +### 6.4 财务重建 dry-run + +先定义本次财务回看区间: + +```bash +RECONCILE_FROM=2026-08-11 +RECONCILE_TO=2026-11-08 +``` + +执行 dry-run: + +```bash +docker compose --profile tools run --rm --no-deps tools \ + npm run finance:rebuild-derived -- \ + --date-from="$RECONCILE_FROM" \ + --date-to="$RECONCILE_TO" +``` + +检查: + +- `closedPeriods` 为空;若包含已关账期间,停止并先走财务确认流程。 +- `scopeItems > 0`。 +- `protectedTotals` 与生产基线一致。 +- `plan` 中的 estimated 快照、销售事实和总账调整数量能够解释。 + +### 6.5 业务配置 + +为完成后台操作,启动 Web;此时公网仍必须停留在维护入口,不能对普通用户开放: + +```bash +docker compose up -d --no-deps --force-recreate web +``` + +由业务负责人完成,脚本不能代替: + +1. 钱包管理 → 补建客服备用金账户并按需拨付。 +2. 商品与成本 → 积分钱包 → 发放三平台积分。 +3. 商品与成本 → 标准商品 → 按需执行「按工单回填商品」和「归一人工商品」。 +4. 商品与成本 → 人工 SKU 配置 → 扫描并逐项配置。 +5. 商品与成本 → 标准商品 → 处理未路由、待分类和人工兜底项。 + +> 迁移 `122` 已负责接单模板商品初始化。本手册不再要求执行旧的 +> “从接单模板初始化”步骤。 + +完成业务配置后重新执行商品路由审计,并记录前后数量变化。 + +### 6.6 财务重建 + +业务配置完成后,重新执行 6.4 的 dry-run,确认 `protectedTotals`、已关账期间和 +计划数量仍符合预期。业务负责人确认后再执行: + +```bash +docker compose --profile tools run --rm --no-deps tools \ + npm run finance:rebuild-derived -- \ + --date-from="$RECONCILE_FROM" \ + --date-to="$RECONCILE_TO" \ + --execute \ + --confirm=REBUILD_DERIVED +``` + +要求: + +- `protectedTotals` 前后完全一致。 +- 订单、订单项、收入、退款、人工支出和资金流水守恒。 +- 已过账总账只追加反向/补差凭证,不删除历史凭证。 +- 所有成本缺口都进入结构化异常,不做自动猜测。 + +再执行一次相同命令验证幂等: + +```bash +docker compose --profile tools run --rm --no-deps tools \ + npm run finance:rebuild-derived -- \ + --date-from="$RECONCILE_FROM" \ + --date-to="$RECONCILE_TO" \ + --execute \ + --confirm=REBUILD_DERIVED +``` + +第二次必须满足: + +- `ledger.adjusted = 0`。 +- `protectedTotals` 再次保持完全一致。 + +然后执行: + +```bash +docker compose --profile tools run --rm --no-deps tools \ + npm run audit:finance -- --strict +``` + +要求 `critical` 为空。 + +## 七、上线验收 + +停止调度任务期间执行: + +```bash +bash deploy/post-deploy.sh \ + --only=verify \ + --launch-at='YYYY-MM-DD HH:mm:ss+08' +``` + +要求: + +- 退出码为 `0`。 +- FAIL 必须为 `0`。 +- WARN 必须逐项记录并由业务负责人签字。 +- `audit:finance --strict` 必须同时通过。 + +2026-11-08 演练中,在做完财务重建后仍有: + +- `07 客服备用金账户已补建`:需在后台完成初始化后才可通过。 +- `15 近 7 天非人工成本覆盖`:演练时为 WARN,原因为 `206`,必须确认成本缺口。 + +## 八、恢复营业 + +验收通过后: + +```bash +docker compose up -d --no-deps --force-recreate web +``` + +然后: + +1. 后台 → 平台配置 → 监控任务 → 开启任务总开关 → 保存。 +2. 确认任务运行状态恢复,且下一次执行时间已生成。 +3. 检查主站、打手端、领取页和订单服务链接。 + +必须完成以下冒烟检查: + +- 后台登录。 +- 订单列表、详情和筛选。 +- 接单工单列表、详情和状态操作。 +- 履约渠道页和商品与成本页签数量一致。 +- 人工 SKU、自动发货、接单平台、第三方卡券和统一结算分类正确。 +- 财务总账试算平衡。 +- 打手端登录、订单页和大厅。 +- 领取链接 `/h5/<订单号>`。 +- 订单服务链接 `/o` 和 `/o/<订单号>`。 + +## 九、观察期 + +恢复营业后: + +- 前 60 分钟持续观察错误率、慢查询、定时任务和订单入单。 +- 前 7 天每天检查商品路由、履约和财务严格审计。 +- 一个完整财务期间持续对平后,才允许讨论删除兼容表、`legacy_*` + 字段和旧脚本。 + +观察期内不得删除: + +- `product_channel_capabilities` +- `manual_sku_registry` 派生行 +- `internal_sku_channels` +- `order_items.legacy_*` +- `fulfillments`、`fulfillment_attempts`、`fulfillment_issues`、`fulfillment_state_events` + +## 十、失败与回滚 + +### 10.1 迁移命令失败 + +如果迁移尚未提交: + +1. 保持 backend/web 停止。 +2. 保存完整迁移日志。 +3. 确认 PostgreSQL 中不存在半提交状态。 +4. 修复迁移或取消本次发布。 +5. 取消发布时,先切回升级前 commit 并重新构建 backend/web,再启动旧后端。 +6. 不得在未确认迁移事务已回滚时启动任何一版后端。 + +### 10.2 迁移已提交但新后端失败 + +硬切后的数据库不兼容旧镜像。选择以下一种: + +1. **修复前进**:保持停服,修复新后端,禁止用旧镜像连接迁移 `150`。 +2. **整库回滚**:使用已通过验证的升级前备份恢复。 + +整库回滚: + +回滚前必须先切回旧代码并重建旧镜像。否则 `restore-db.sh` 会用当前新镜像补迁移, +把刚恢复的 `146` 再次升级到 `150`。 + +```bash +git switch --detach '<升级前 commit>' +docker compose build backend web +bash deploy/restore-db.sh \ + --key '<升级前最终备份 OSS key>' \ + --config-key '<对应配置备份 OSS key>' \ + --yes +``` + +恢复后: + +1. 确认最高迁移为 `146`。 +2. 启动 backend/web。 +3. 执行健康检查、登录和近期订单抽查。 +4. 恢复调度任务总开关。 + +> 当前恢复脚本只有在 4.3 硬门禁通过后才可用于回滚。 +> 恢复脚本失败时,数据库可能处于部分恢复状态,必须立即停止业务并人工处理。 + +### 10.3 财务重建失败 + +财务重建是幂等的数据阶段,不应因为一次失败立即整库回滚: + +1. 保持停服和调度关闭。 +2. 保存失败 JSON、日志和当前 protected totals。 +3. 修复数据或脚本后重跑。 +4. 只有原始订单、退款、资金流水发生不守恒或数据库不可用时才考虑整库回滚。 + +### 10.4 验收失败 + +验收 FAIL 或关键审计异常时不得恢复营业: + +1. 保持 backend 健康但 Web 不开放。 +2. 调度保持关闭。 +3. 按失败项修复或走整库回滚。 +4. 修复后从对应阶段重新执行,不跳步。 + +## 十一、发布记录模板 + +```text +发布日期: +发布负责人: +数据库执行人: +验收人: +业务负责人: + +升级前 commit: +目标 commit: +升级前迁移: +目标迁移: + +升级前备份 key: +升级前备份 sha256: +升级前配置备份 key: +恢复验证结果: + +维护开始时间: +迁移开始时间: +迁移结束时间: +统计刷新结束时间: +财务重建结束时间: +验收结束时间: +恢复营业时间: + +商品路由审计: +履约审计: +财务 dry-run: +第一次财务重建: +第二次财务重建: +财务严格审计: +上线验收 FAIL: +上线验收 WARN: + +已知待分类项及负责人: +最终结论:继续 / 暂停 / 回滚 +``` diff --git a/docs/统一订单履约与财务模型-权威版.md b/docs/统一订单履约与财务模型-权威版.md index b7b91409..3519527f 100644 --- a/docs/统一订单履约与财务模型-权威版.md +++ b/docs/统一订单履约与财务模型-权威版.md @@ -3,7 +3,7 @@ type: 权威架构与实施计划 description: 订单、内部 SKU、履约路由、履约执行、商品、成本与财务的唯一权威口径及重构计划。 tags: [订单, 内部SKU, 履约, 商品, 成本, 财务, 迁移] status: authoritative -version: 2.3 +version: 2.4 updated_at: 2026-11-08 --- @@ -12,7 +12,7 @@ updated_at: 2026-11-08 > 状态:**唯一权威文档**。 > 最后校准:2026-11-08。 > 生产基线:迁移 `146`。 -> 开发基线:迁移 `149`(仅本地,尚未部署生产)。 +> 开发基线:迁移 `150`(仅本地,尚未部署生产)。 > > 从本文件生效起,订单、内部 SKU、履约渠道、履约载体、标准商品、成本和财务相关实现, > 只能以本文件为业务口径。其他旧分析只作为历史资料,不再具备设计或验收效力。 @@ -332,7 +332,7 @@ finance_journal_* -- 总账凭证,可重建 2026-11-08 生产只读核对结果: -- 生产迁移 `146`,本地开发基线迁移 `149`。 +- 生产迁移 `146`,本地开发基线迁移 `150`。 - 启用标准商品 `1127`:`aff:` 30、`tpl:` 903、`tpv:` 46、`man:` 148。 - `man:` 商品中只有 105 个当前主渠道为人工;41 个已走其他主渠道,2 个无主渠道。 - 人工页头部显示 `107`,商品列表显示 `148`,相差的 41 个就是跨渠道旧商品。 @@ -600,6 +600,8 @@ assigned 且 carrier_status=missing 必须进入异常队列 任务/工单只维护尝试历史;订单项旧履约列冻结并统一读取 `v_order_item_current`。 - 迁移 `149`:建立 `v_finance_order_item_scope`,统一销售事实、重建与审计的收入、 退款、商品、渠道和业务日口径,并增加销售凭证按日查询索引。 +- 迁移 `150`:核销任务允许订单级来源;`收到订单后`由订单入库事件直接入队, + 使用 `order:<订单ID>` 去重,不读取履约渠道、载体或订单项状态。 - 商品列表、总览、渠道映射和商品下拉统一读取商品当前渠道。 - 运行期代码停止写入 `product_channel_capabilities`,该表仅保留到兼容期结束。 - 只读审计:`npm run audit:product-routing -- --strict`。 @@ -607,7 +609,7 @@ assigned 且 carrier_status=missing 必须进入异常队列 - 自动/卡券/接单主渠道商品的人工配置均保留为显式 `fallback`,没有被误升为人工主渠道。 - 1 个无主路由、无人工配置、无启用映射的 `man:` 商品已归档,未删除历史引用。 - 旧 `internal_sku_channels` 已冻结:运行时不再读、不再写,也不再有视图或触发器依赖。 -- 空库从迁移 `001` 顺序执行到 `149` 已通过;新库不再保留任何旧履约影子同步触发器。 +- 空库从迁移 `001` 顺序执行到 `150` 已通过;新库不再保留任何旧履约影子同步触发器。 - 无主路由的活动商品会进入“未路由”,不自动猜测渠道。 当前本地仍需业务处理的数据: @@ -667,7 +669,7 @@ assigned 且 carrier_status=missing 必须进入异常队列 5. 全部业务读 SQL 切到 `v_order_item_current`,禁止直接读取订单项旧状态列。 6. 状态事件、任务尝试、工单尝试和配置变化触发器统一围绕正式履约表工作。 7. 真实 PostgreSQL 集成测试覆盖状态写入、幂等、事件留痕、当前尝试和配置关联。 -8. 新空库 `001 → 149` 与迁移 `145` 备份升级库均完成硬切验证。 +8. 新空库 `001 → 150` 与迁移 `145` 备份升级库均完成硬切验证。 验收: @@ -745,6 +747,8 @@ npm run audit:finance -- --strict - 数据库迁移由 `154–168` 重组为 `147–149`:内部 SKU/商品路由、履约执行、 财务派生三段各一个文件;未迁入旧 `dev` 的 `147–153`。 +- 迁移 `150` 增加订单级收单核销来源。订单入库后仅按平台策略投递 + `order_received` 任务,不再依赖渠道或载体;旧 `unmatched` 开启时自动继承一次。 - 删除会直接写已改名订单项列的 `reclassify-post-cutover.ts` 和 `merge-duplicate-products.ts`。 - 删除重复的 `finance:rebuild-cutover` 入口,部署脚本统一调用 @@ -760,7 +764,7 @@ npm run audit:finance -- --strict 生产升级演练(本地临时库): - 输入:`backups/postgres/order_site-20261106-192520.dump`,备份迁移 `145`; - 恢复后执行 `146 → 149`。 + 恢复后执行 `146 → 150`。 - 数据规模:订单 `456777`、订单项 `456762`、商品 `1572`、正式履约 `456762`。 - 升级和迁移全部成功;恢复时唯一失败的是备份自身依赖顺序导致的函数索引, 在函数恢复后按原定义补建,随后迁移 `148` 正常删除该索引。 @@ -867,11 +871,12 @@ docs/archive/ 本地阶段 0–6 已按硬切方案完成,生产仍停留在迁移 `146`。后续顺序固定为: -1. 生产执行前重新生成全量备份,并核对备份迁移号确为 `146`。 -2. 在生产依次执行迁移 `147 → 149`,每阶段运行对应严格审计; - 无法确定的数据进入未路由或待分类,不做猜测。 -3. 执行一次 `finance:rebuild-derived` 的 dry-run,核对 A 级数据守恒后, - 再按窗口执行 `--execute --confirm=REBUILD_DERIVED`。 -4. 观察一个完整财务期间,确认页面、履约、事实和总账持续对平。 -5. 观察期结束后,再单独发布兼容表、旧脚本别名和 `legacy_*` 字段的删除迁移。 +1. 生产升级只按 [生产升级操作手册](生产升级操作手册.md) 执行。 +2. 升级前备份必须完成隔离恢复验证;恢复验证未通过时禁止迁移。 +3. `147 → 150` 作为同一硬切迁移组执行,迁移后刷新统计并完成商品、履约、 + 财务三组严格审计;无法确定的数据进入未路由或待分类,不做猜测。 +4. 执行 `finance:rebuild-derived` 的 dry-run,核对 A 级数据守恒后, + 再按窗口执行 `--execute --confirm=REBUILD_DERIVED`,并验证第二次重建幂等。 +5. 观察一个完整财务期间,确认页面、履约、事实和总账持续对平。 +6. 观察期结束后,再单独发布兼容表、旧脚本别名和 `legacy_*` 字段的删除迁移。 diff --git a/docs/部署启动与数据库重建.md b/docs/部署启动与数据库重建.md index b52f9f64..00aa13ba 100644 --- a/docs/部署启动与数据库重建.md +++ b/docs/部署启动与数据库重建.md @@ -219,7 +219,7 @@ docker compose ps docker compose logs -f backend ``` -## 财务/成本上线后的初始化(当前开发基线 149) +## 财务/成本上线后的初始化(当前开发基线 150) 代码与迁移只负责建表;**存量订单回填、备用金账户、积分余额、商品目录、人工 SKU 不会自动创建**。 部署成功后按「回填 → 商品初始化 → 财务同步 → 验收」四步走: @@ -228,7 +228,7 @@ docker compose logs -f backend ```bash bash deploy/post-deploy.sh --dry-run # 先看计划与待回填量 -bash deploy/post-deploy.sh --only=backfill # ① 收入回填 + ② 履约方式回填 +bash deploy/post-deploy.sh --only=backfill # ① 收入回填 + ② 订单级收单核销补投 ``` > 不要在商品目录为空时直接跑 `--only=sync`:财务同步会为每个未归一订单项写一条 @@ -288,6 +288,10 @@ cd /home/order_site && git pull bash deploy/ubuntu-deploy.sh # 想看这次会做什么:加 --dry-run ``` +如果本次包含数据库迁移、履约/商品路由切换或财务派生数据重建,不要直接执行上述命令, +必须按照 [生产升级操作手册](生产升级操作手册.md) 的停写、备份验证、迁移、审计、 +财务重建和验收顺序执行。 + 后端启动时自动执行未应用的增量迁移,一般不必单独跑迁移。 确认: