# ARR 2.0 单机 Compose 部署

本 Compose 模板部署两个 HTTP 服务：`web` 负责应用登录、页面、程序化 XML 处理、验证和入库，`caddy` 负责 HTTPS。没有 Agent 回调服务或 MCP 公网入口。自动月报由另一个无端口的 `monthly_reports.worker` 进程执行，不得放进 Web 请求进程。

## 前置条件

- DNS：`WEB_PUBLIC_HOST` 指向部署主机；
- PostgreSQL 15+：目标必须是隔离数据库 `booking_test`，发布窗口内按 016 → 017 → 018 应用；
- OSS：区域匹配、服务端加密、未启用或暂停 versioning、禁止匿名写；bucket ACL 可为 private 或 public-read；
- ARR 数据库账号和 OSS RAM/STS 凭据由 Secret 管理器注入，不写入镜像或仓库。
- 月报和公司渠道明细均由 Python/openpyxl 生成；不需要 Node.js、npm 或私有运行时。
- 月报/公司 XLSX 与 `result.json` 上传现有 OSS；只有 `.web-jobs` 队列状态和临时 staging 继续使用 `/app/outputs` 持久卷。

## 配置

```bash
cp deploy/.env.production.example deploy/.env.production
chmod 600 deploy/.env.production
```

填写应用登录账号/长随机密码、数据库、OSS 和域名。登录凭据必须由 Secret 管理器注入，不能写入镜像或提交到仓库。ARR 2.0 不需要以下配置：

- SuperAgent/Open Agent URL 或 API key；
- MCP 域名、Bearer 或并发参数；
- Agent callback URL/HMAC；
- `fetch_oss_file` 或源文件公网 URL。

## 应用 016–018 日报价格复核迁移

在目标数据库确认当前连接为隔离的 `booking_test` 后，先应用 016；脚本会自行拒绝其他数据库。016
只替换月报发布校验函数，不新增表或列，历史 `local` 工件不需要迁移。随后应用 017，再应用修复人工清单
工件类型的 018；各迁移都会拒绝非 `booking_test`、缺失前置对象或重复应用：

```bash
psql "$ARR_DATABASE_URL" -Atc "select current_database();"
sha256sum database/016_monthly_report_oss_artifacts.sql
psql "$ARR_DATABASE_URL" -v ON_ERROR_STOP=1 \
  -f database/016_monthly_report_oss_artifacts.sql
sha256sum database/017_daily_price_review.sql database/017_daily_price_review.down.sql
sha256sum database/018_daily_review_manual_override_artifact.sql \
  database/018_daily_review_manual_override_artifact.down.sql
```

017 会改变 lifecycle/Finance 约束并新增不可删除的复核审计事实。正式发布前，操作员必须在受控备份中保存
schema/data manifest。优先在恢复出的临时 `booking_test` 副本上按顺序应用 016 → 017 → 018、检查约束/最小权限、
再运行受保护的 down rollback probe。如果受控环境没有副本能力，且目标库尚无任何 review/manual 事实，可由经过审阅的迁移工具
只在内存中移除 up/down 最外层 `BEGIN`/`COMMIT`，将两份正文置于同一外层事务，验证 up 中间态、down 精确恢复及
第二连接零残留后强制回滚；不得改写迁移文件，也不得直接在正式库执行原始 down 文件。确认探针通过后，才执行：

```bash
psql "$ARR_DATABASE_URL" -v ON_ERROR_STOP=1 \
  -f database/017_daily_price_review.sql
psql "$ARR_DATABASE_URL" -v ON_ERROR_STOP=1 \
  -f database/018_daily_review_manual_override_artifact.sql
```

一旦已产生 review case、冻结清单或人工定价 lineage，`017_daily_price_review.down.sql` 会故意拒绝破坏性回滚；
此后只能用前向修复迁移。018 只允许新增 `manual_override_json` 工件类型；其 down 文件在已经登记任何该类
不可变工件时也会拒绝回滚。已有 017 但尚未应用 018 的环境，应直接备份、探针并前向应用 018，不得改写已应用
的 017。

## 校验并启动

```bash
docker compose --env-file deploy/.env.production config --quiet
docker compose --env-file deploy/.env.production build web
docker compose --env-file deploy/.env.production up -d
```

Compose 启动后，应由 systemd、容器编排器或同等进程管理器独立执行月报 worker：

```bash
docker compose --env-file deploy/.env.production run --rm web \
  python -m monthly_reports.worker \
  --output-root /app/outputs/monthly_reports
```

该命令沿用 Compose 注入的 `ARR_DATABASE_URL`、`ARR_OSS_*` 和 OSS 凭据；如果改由主机上的 systemd 运行，
则在进程环境中提供同一组变量，或显式传入受控的 `--db-config` 文件。`--once` 可用于部署后的单次探针：

```bash
docker compose --env-file deploy/.env.production run --rm web \
  python -m monthly_reports.worker \
  --once \
  --output-root /app/outputs/monthly_reports
```

检查：

```bash
curl --fail --silent "https://$WEB_PUBLIC_HOST/healthz"
docker compose --env-file deploy/.env.production logs --tail=100 web
```

`/healthz` 仅以 HTTP 200/503 表示数据库与处理入口是否就绪，不暴露组件详情；详细 `/api/health` 必须登录后访问。worker 是独立进程，应另行监控其存活和 outbox 的 `pending/publishing/dead` 数量。失败时优先检查登录环境变量、数据库目标、OSS 区域/加密/versioning、OSS 凭据和固定处理器；无需排查 Agent、MCP 或 Node/npm。

## 验收

使用无真实住客隐私的受控 XML：

1. 未登录访问首页应进入 ARR 登录页，错误凭据不得创建会话；
2. 使用授权 Finance 账号登录，页面上传一次 XML；
3. 上传响应应直接给出 `succeeded`、`needs_review` 或 `failed` 以及 `job_id`；
4. 在任务日志中确认“固定处理器已启动 → 程序输出制品已登记 → 独立验收 → Finance 提交”；
5. 纯 `PRICE_UNMATCHED` 任务应显示待人工处理进度、无日报下载、无 Finance 版本、无 `arr.processing_failed`/月报事件；人工价格只接受非负整数（显式零写 `0`，系统冻结为 `0.00`），填完全部价格后才可确认生成；
6. 最终成功任务应能下载对应日报，数据库中 source/retained/outcome 数量、`manual_review` 行数和冻结清单 SHA-256 必须与结构化结果一致；
7. worker 应仅消费最终成功的对应 outbox 事件，页面显示真实月报 ID/版本/“更新至”，下载文件哈希应与 OSS 登记值一致；删除 Web 容器或清空其临时 `outputs/monthly_reports` 后，月报与公司报表仍应可下载；
8. 退出登录后，页面、API 和下载均应重新要求登录；
9. 业务失败任务不得激活 Finance 当前版本或触发月报。监控待复核数量/停留时长、人工定价行数和生成重试次数。

## 回退

ARR 2.0 与 `/Users/chillishark/ARR项目0727` 是两个独立目录。应用级回退可停止本 Compose 并重新启动原工程；不要为了应用回退而删除数据库 009/010 表或 OSS 工件。数据库破坏性回滚需要单独审批、备份和迁移演练。
