Files
FruitServer/DEPLOY-CHECKLIST.md
jianghaifeng 7dd4b15804 docs(deploy): 按 specs 约束补全部署文档(新增 DEPLOY-CHECKLIST.md)
为什么:用户要求"写好文档,一定要根据 specs 文档约束好"。把 specs 里与部署相关的
硬约束逐条对照现状后发现 9 条里有 6 条未覆盖或只写了一半,需要一次补齐并如实标注工程缺口。

- 新增 DEPLOY-CHECKLIST.md:环境映射(办公室主机 = test)、域名/HTTPS/反代(含 nginx 与证书建议)、
  网络分区、发布与回滚规范(语义化 tag / release→main / 回滚演练)、备份与恢复(specs 目标 vs 现状差距)、
  监控与告警现状、密钥托管与轮换、**上线检查清单对照表(docs/12 逐条 ✅⚠️❌ + 责任方)**、
  风险/待确认(R1–R10)、上线前必补清单(P0–P2)
- DEPLOY.md 增加"本机属于 test 环境"与交叉引用,并单列"上线前必须补的四条"提示
- 对照表里的 ✅ 都带证据(如分区生效先实测 playback_ticket_202610 落分区、_default = 0)
- 只写文档,未改任何实现;mvn test 83 条全过、两入口 package 通过
2026-10-16 13:20:31 +08:00

257 lines
23 KiB
Markdown
Raw Permalink 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.
# Author: fruit.com.cn
#
# DEPLOY-CHECKLIST —— 部署合规视角(specs 约束逐条结论 + 上线检查清单对照 + 风险/待确认)
#
# 这份文档回答两个问题:
# 1) `docs/04-部署架构`、`docs/08-备份恢复方案`、`docs/10-安全方案`、`docs/11-发布与回滚方案`、
# `docs/12-上线检查清单`、`docs/07-监控告警方案`、`GIT_WORKFLOW.md`、`CODING_STANDARDS.md`
# 这些 specs 里跟部署有关的要求,**我们现在满足到哪一步**;
# 2) 上线前**必须补什么**、谁来补。
#
# 操作层面的"怎么部署/怎么回滚命令"在 `DEPLOY.md`(本文件只做结论与对照,避免两份口径分叉)。
# 结论用三态:✅ 已满足 | ⚠️ 部分满足(能用但有差距)| ❌ 未满足(上线前必补)
## 一、环境映射(对应 `04-部署架构`§一)
`04-部署架构`§一把环境分成四级:
| 环境 | 用途 | 数据 | 访问 |
| --- | --- | --- | --- |
| dev | 本地开发 | 本地 PostgreSQL | 内网 |
| test | 测试/联调 | 独立测试库 | 内网/VPN |
| pre | 预发(生产镜像演练) | 接近生产 | 受限 |
| prod | 生产 | 生产库 | 公网 |
**当前这台办公室主机 = `test` 环境**,依据三条:
1. 用途是"给前端联调 + 内部验收",不是对外生产(`04-部署架构`§一 test = 测试/联调);
2. 访问方式计划是内网/办公网(`admin-api` 只绑 `127.0.0.1`),而不是公网;
3. 用的是独立的开发库(`fruitshorts`,含种子数据),不是生产库。
规范要求与我们的对应:
- `docker-compose.yml` 用 **Docker Compose 单机编排** → 对应 §五「第一阶段(dev/test):Docker Compose 单机编排」,✅。
- 一台机器上合并 app/api/DB/Redis + worker 独立容器 → 对应 §三「精简方案:test 环境可将 app/api/DB/Redis 合并到 1 台 8C32G,转码 worker 独立 1 台」,✅(worker 是独立容器,但**与在线服务同机**:资源隔离靠容器限额,真正的分机留到生产)。
- **差距**:`pre` 环境(生产镜像演练)**尚未建立** —— 上线前建议在目标机用同一份 compose 跑一次"预发演练",即用生产镜像 + 接近生产的数据走一遍发布流程。状态:❌(流程未建立,不阻塞 test 联调)。
## 二、域名 / HTTPS / 反向代理(对应 `04-部署架构`§四 + `10-安全方案`§二)
**specs 原文**:`04-部署架构`§四「域名/SSL:管理端、客户端 API、媒资**分别域名**;**HTTPS 强制**」;
`10-安全方案`§二「传输安全:HTTPS 强制;密钥环境变量注入」。
**当前状态:❌ 未满足**。现在的形态是 `IP:端口 + HTTP`:
| 需要的域名 | 现状 | 上线前必须做 |
| --- | --- | --- |
| 管理端(后台 Web + `/admin/**`) | 无域名;`admin-api` 绑 `127.0.0.1:8080`,靠内网/SSH 隧道访问 | 申请域名(如 `admin.<domain>`)+ 证书;nginx 反代 → `127.0.0.1:8080`;**并保持只允许内网/VPN 来源**(见 §三) |
| 客户端 API(`/app/**`) | 无域名;`app-api` 监听 `8081`(HTTP) | 申请域名(如 `api.<domain>`)+ 证书;nginx 反代 → `8081`,只暴露 `/app/**`;**强制 HTTP→HTTPS 跳转** |
| 媒资(`/files/**`) | 无域名;与 app-api 同一端口 | 申请域名(如 `media.<domain>`)或直接走 CDN 域名;**播放地址已带签名参数**,CDN 需按签名做缓存策略(`04-部署架构`§四) |
**测试环境(本机)可以先用内网 HTTP 联调**(`04-部署架构`§一 test 是内网/VPN 访问),
但**上线(pre/prod)前必须满足上述三条 HTTPS + 独立域名**,否则不满足 `12-上线检查清单`§三「传输安全」。
### nginx + 证书的建议做法(上线前照做)
```nginx
# /etc/nginx/conf.d/fruitshorts.conf(示意;域名与证书路径按实际替换)
server { # 客户端 API
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
client_max_body_size 20m; # 分片上传,与 admin-api 的 multipart 上限一致
location / { proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme; }
}
server { # 管理端:只允许内网/VPN 网段
listen 443 ssl http2;
server_name admin.example.com;
allow 10.0.0.0/8; allow 192.168.0.0/16; deny all; # 按实际内网段调整
location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme; }
}
server { # HTTP 强制跳 HTTPS
listen 80;
server_name api.example.com admin.example.com;
return 301 https://$host$request_uri;
}
```
证书建议用 **Let's Encrypt(certbot)** 自动续期(`certbot --nginx -d api.example.com -d admin.example.com`),
或公司自有证书;无论哪种,**到期前必须能自动续**,否则接口会整体不可用。
反代配置文件需与本仓库一起纳入版本管理(放目标机的 `/etc/nginx/conf.d/`,另存一份到运维仓库)。
## 三、网络分区(对应 `04-部署架构`§四)
**specs 原文**:「网络分区:管理后台仅内网/VPN 可访问;客户端 API 公网。」
| 要求 | 现状 | 上线前必须做 |
| --- | --- | --- |
| 管理后台仅内网/VPN | ✅ 已满足:`docker-compose.yml` 里 admin-api 端口映射为 `127.0.0.1:8080:8080`(只绑本机,公网访问不到) | 上公网后仍要保证:只经 nginx + `allow/deny` 内网段暴露(见 §二示例) |
| 客户端 API 公网 | ⚠️ 部分满足:`app-api` 监听 `0.0.0.0:8081`,同网段可访问;**但还没有反代/域名,也没限制只暴露 `/app/**`** | 反代只放行 `/app/**`(`/files/**` 走媒资域名/CDN);不要把 `/v3/api-docs`、`/healthz` 直接暴露到公网(`/healthz` 可留给内网监控) |
**注意**:`/doc.html` 与 `/v3/api-docs` 由 `SWAGGER_ENABLED` 控制(见 `DEPLOY.md`)。
**联调环境建议 `true`**(前端要看),**公网生产建议 `false`**(不暴露接口清单)。
## 四、发布与回滚规范(对应 `11-发布与回滚方案`§一/§四 + `GIT_WORKFLOW.md`§四)
**specs 原文**:
- `11-发布与回滚方案`§一:「语义化版本 `v<major>.<minor>.<patch>`;分支 `main`(生产)← `release/*` ← `develop` ← `feature/*`;每次 `release`/`hotfix` 合并 `main` 必须打 tag,tag 不可移动/删除。」
- `GIT_WORKFLOW.md`§四:「`major` 破坏性变更、`minor` 新功能、`patch` 修复;发布后 tag 不可移动、不可删除(错误版本发新 tag 修正,不覆盖旧 tag)。」
- `11-发布与回滚方案`§四:「回滚演练:**上线前演练回滚**,确保可执行。」「数据库迁移脚本必须可回滚(向前兼容,避免破坏性变更)。」
| 要求 | 现状 | 上线前必须做 |
| --- | --- | --- |
| 语义化 tag `v<x.y.z>` | ⚠️ 部分满足:`DEPLOY.md` 里只是示例式地提到"每次发版打 tag",**没有命名规范** | 按 §四 规则打:首个上线版本 `v1.0.0`;之后功能 `v1.1.0`、修复 `v1.0.1`;**错误版本发新 tag,绝不移动/删除旧 tag** |
| release/hotfix 合并 main | ⚠️ 流程未走过:目前工作都在 `develop`,`main` 未动 | 上线时:`develop` → 建 `release/v1.0.0` → 在 test 环境验证 → 合并 `main` + 打 tag `v1.0.0`(**由用户操作,Codex 不碰 main**) |
| 回滚可执行 | ✅ 技术路径已写全(`DEPLOY.md`§七:切回上一个镜像 tag / git 回上一个 release tag + `up -d --build` + 重跑冒烟) | **上线前演练一次**(在 test 环境故意回滚到上一版本并跑 `tools/smoke.sh`),把演练记录留档 |
| 数据库迁移可回滚 | ⚠️ 部分满足:`sql/*.sql` 全是幂等增量(`ADD COLUMN IF NOT EXISTS`、`CREATE TABLE IF NOT EXISTS`),**没有破坏性变更**;`upsert` 语义的种子脚本也在 | 上线前确认:本次发布**不包含** `DROP/ALTER TYPE` 类破坏性语句(当前没有);如将来有,必须先写"回滚脚本 + 备份"再发 |
| 灰度/放量 | ❌ 未满足(单机 Compose 无灰度能力) | 上线首日按"先内测 → 再小流量(如有网关可切)";正式灰度需要 K8s/网关,属后续阶段(`04-部署架构`§五:生产上 K8s) |
## 五、备份与恢复(对应 `08-备份恢复方案`§二/§三)
**specs 目标**:全量每日 × 保持 30 天;**WAL 归档(实时)**;可选增量;**备份加密 + 异地存储**;
**RPO ≤ 5 分钟、RTO ≤ 30 分钟**;**每季度至少一次恢复演练**。
| 项 | specs 要求 | 现状 | 差距 / 上线前必须补 |
| --- | --- | --- | --- |
| 全量备份 | 每日 1 次,保留 30 天 | ⚠️ `DEPLOY.md`§七 给了**手动** `pg_dump` 命令;**没有定时任务、没有保留策略** | 目标机加 cron(或 compose 侧容器)每日 `pg_dump`,按日期命名保留 30 天;脚本可放 `tools/backup.sh`(本轮未写,属运维脚本) |
| WAL 归档 | 实时,用于 PITR | ❌ 未配置(PG 默认 `archive_mode=off`) | `postgresql.conf`:`wal_level=replica`、`archive_mode=on`、`archive_command` 推到异地;这是达成 **RPO≤5min** 的唯一途径 |
| 增量 | 可选 | ❌ 未做 | 有了 WAL 归档即可满足 RPO,增量备份可后补 |
| 加密 | 备份加密 | ❌ 未做 | `pg_dump` 管道给 `age`/`gpg`,或落到加密卷;密钥单独保管(与备份分开) |
| 异地 | 与生产不同可用区/账号 | ❌ 未做(本机 dump 还在同一台机器上) | 推到对象存储(另一账号/另一区);**"备份和数据库在同一台机器上"等于没备份** |
| 恢复演练 | 每季度 ≥ 1 次,记录归档 | ❌ 从未做过 | 首次上线前做一次"恢复到新库 + 起服务 + 冒烟",记录耗时(验 RTO)与丢失窗口(验 RPO) |
**结论:备份恢复这一项与 specs 差距最大,是上线前必须补的硬缺口(❌)。**
当前只有"能手动 dump",距离「RPO≤5min / RTO≤30min / 加密 / 异地 / 季度演练」还差整套机制。
建议排期:**上线前**至少做到「每日全量 + 异机保存 + 一次恢复演练」;**上线后两周内**补 WAL 归档与加密。
## 六、监控与告警现状(对应 `07-监控告警方案`§七)
**specs 原文**:`07-监控告警`§七「Prometheus/Loki/Grafana 尚未部署,**第一阶段先用日志 + `SlowRequestLogger` 兜底**」;
§六「告警渠道:飞书/钉钉/邮件 + 值班轮转」;§六「值班表与 Runbook 见 `docs/plans/`(上线前补)」。
| 项 | 现状 | 上线前必须补 |
| --- | --- | --- |
| 指标(Prometheus) | ❌ 未部署;**项目也没引 actuator**(`/actuator/health` 一直是未映射,见 `DEPLOY.md`§五说明) | 按 §七 的既定路线部署;要暴露指标需先给两个入口加 `spring-boot-starter-actuator` + `micrometer-registry-prometheus`(**属代码改动,需立项**) |
| 日志聚合(Loki) | ❌ 未部署;现在日志在容器 stdout(`docker compose logs`) | 部署 Loki + Promtail 采集容器日志 |
| 大盘(Grafana) | ❌ 未部署 | 按 §五 大盘定义建面板 |
| 第一阶段兜底 | ✅ 已具备:应用结构化日志(转码耗时/失败原因、业务异常、`SlowRequestLogger` 慢请求告警都在代码里)+ 容器级 `healthcheck`(`/healthz`) | 至少把"容器 unhealthy"接到人的手机上(可用 `docker compose events` + 简单脚本/飞书机器人) |
| 告警渠道 + 值班 + Runbook | ❌ 未定 | 定渠道(飞书/钉钉/邮件)、排值班表、把 `DEPLOY.md`§九「故障速查」扩成 Runbook |
**结论:监控告警 ❌ 未满足**,但按 specs §七 的既定节奏,**第一阶段允许先用日志兜底** ——
前提是"告警能到达人"。上线前最低要求:**容器 unhealthy / 冒烟失败能被发现**(哪怕先是人工每天跑一次 `tools/smoke.sh`)。
## 七、密钥托管与轮换(对应 `10-安全方案`§五)
**specs 原文**:§五「密钥托管:生产密钥**集中托管**,**最小权限**」;`12-上线检查清单`§三「JWT/签名密钥走环境变量,未硬编码/入库」「生产密钥集中托管」。
| 要求 | 现状 | 上线前必须补 |
| --- | --- | --- |
| 密钥走环境变量、未硬编码/入库 | ✅ 已满足:三个可运行件的密钥全部 `${ENV:...}` 注入;`.env` 已 gitignore;`.env.example` 只有占位;镜像里不含密钥 | 继续保持;**代码审查时禁止把密钥写回 yml** |
| 缺失即启动失败 | ✅ 已满足:`ProdSecretGuard` 在 `prod` profile 下校验三个密钥 + 签名开关 + DB_URL + CORS,缺失/仍是 dev 默认值/不足 32 位 → 退出码 1 | — |
| **集中托管** | ⚠️ 部分满足:现在是"目标机的 `.env` 文件"(文件权限 600),**没有密钥管理系统** | 短期:`.env` 权限 600、只有运维账号可读、**不进任何 git 仓库**、单独备份(加密);中期:迁到 K8s Secret / 云 KMS / Vault(`04-部署架构`§五 生产路线也是 K8s Secret) |
| 最小权限 | ⚠️ 部分满足:DB 用单一 `fruitshorts` 账号(读写全库) | 拆账号:应用用 `fruitshorts_app`(DML 权限、无 DDL);备份用只读账号;DDL 只留给运维 |
| 轮换 | ❌ 未做(也没有流程) | 定义轮换周期(建议 90 天或按事件):**先加新密钥 → 双密钥并行验证期 → 再下线旧密钥**;JWT 轮换会导致存量 token 失效,需选低峰并公告(`10-安全方案`§一 token 有效期 120min,影响窗口可控) |
## 八、上线检查清单对照表(逐条映射 `docs/12-上线检查清单.md`)
> 三态:✅ 已满足 | ⚠️ 部分满足 / 有条件满足 | ❌ 未满足(上线前必补)
> 「责任方」列区分:**Codex**(我能做的)/ **用户或运维**(需要账号、域名、外部资源)。
### 一、代码与门禁
| 检查项 | 状态 | 依据 / 缺口 | 责任方 |
| --- | --- | --- | --- |
| 三端门禁全绿 | ⚠️ | **后端 ✅**:`mvn test` 83 条全过;**前端(Flutter/Web)不在本工程范围**,由另一同事的 AI 负责,需其提供门禁结果 | 用户/前端 |
| 接口契约 `api-phase1.md` 已同步 | ✅ | 本轮 B1-1/B1-2 的 verify / reconcile 改动都已同步进契约 | Codex |
| 作者标识齐全 | ✅ | 远端已做全量补齐(`35be37c`),本轮新增文件也都带 `// Author: fruit.com.cn` | Codex |
| 代码规模红线、无关键路径 TODO | ⚠️ | 未做静态检查工具(后端无 checkstyle/pmd),**存量未逐文件核对**;无 TODO 扫描证据 | Codex(需立项) |
### 二、功能与测试
| 检查项 | 状态 | 依据 / 缺口 | 责任方 |
| --- | --- | --- | --- |
| 核心用例通过、P0 清零 | ⚠️ | 后端 83 条(含真实 ffmpeg smoke)全过;**前端与端到端用例不在本工程** | Codex/用户 |
| 支付闭环端到端 | ⚠️ | 票据校验/幂等/退款回收/对账**已实现并有单测**;**真实 Google Play 未联调**(缺 packageName + service account JSON,属付费凭据) | 用户(凭据)+ Codex(联调) |
| 播放长稳 + 弱网测试 | ❌ | 未做(需真机 + 网络模拟) | 前端/用户 |
| 多端(iOS/Android)回归 | ❌ | 未做(Android 无可用模拟器,iOS 有真机) | 前端/用户 |
### 三、安全
| 检查项 | 状态 | 依据 / 缺口 | 责任方 |
| --- | --- | --- | --- |
| 播放签名 `signature-required=true`(生产必开) | ✅ | `application-prod.yml` 写死 `true`,`ProdSecretGuard` 再校验一次,缺了直接拒启 | Codex |
| JWT/签名密钥走环境变量,未硬编码/入库 | ✅ | 三件全 `${ENV:...}`;`.env` gitignore;镜像不含密钥 | Codex |
| 越权/注入/敏感数据专项测试 | ⚠️ | **越权**:创作者中心有专项用例(B 批);**注入/SQL 注入**:全部走 MyBatis-Plus 参数绑定,无字符串拼接 SQL(人工核对),**无自动化用例**;敏感数据脱敏有实现(`AppIdentifier.mask`)但无专项测试 | Codex(需立项) |
| 生产密钥集中托管 | ⚠️ | 见 §七:现为 `.env` 文件权限隔离,未接 KMS/Vault | 用户/运维 |
### 四、基础设施
| 检查项 | 状态 | 依据 / 缺口 | 责任方 |
| --- | --- | --- | --- |
| 数据库分区已生效 | ✅ | `watch_history`/`playback_ticket`/`order_event` 均为声明式月分区,`partition-maintenance` 启动时+每日补齐。**实测证据**:`select tableoid::regclass, count(*) from playback_ticket group by 1` → `playback_ticket_202610 \| 1`(落月分区,不是 `_default`);`select count(*) from watch_history_default` → `0` | Codex |
| Redis/缓存(如接入)配置正确 | ⚠️ | compose 已备 Redis 容器,**代码尚未接入(C2 待做)**;当前不依赖 Redis | Codex(C2) |
| 媒资已迁 OSS/S3 + CDN(生产) | ❌ | **仍是本地磁盘 `./data/uploads`,与在线服务同机**;`MediaStorageService` 已把存储收敛到一处,切换 OSS 只需改它 + 转码产物上传路径 | 用户(账号)+ Codex |
| 备份恢复已配置并演练 | ❌ | 见 §五,差距最大 | 用户/运维 + Codex(脚本) |
| 监控告警已接入、告警可达 | ❌ | 见 §六 | 用户/运维 |
### 五、商店与合规
| 检查项 | 状态 | 依据 / 缺口 | 责任方 |
| --- | --- | --- | --- |
| 应用名称/截图/文案/隐私标签一致 | ❌ | 未办(客户端上架材料) | 用户 |
| App Privacy / Data Safety / SDK 清单 | ❌ | 未办 | 用户 |
| 隐私政策、用户协议已上线 | ❌ | 未办(后端有 `app_user_consent` 落库能力,文案与页面需法务/前端) | 用户 |
| 版权/素材/商标授权台账 | ❌ | 未办 | 用户 |
| 商店账号/证书/包名有效 | ❌ | 未办;**并且包名仍不一致**(Android `com.stormg.lumadrama` / iOS `com.stormg.lumadramv`,见 C 端回执) | 用户(建议上架前统一) |
### 六、发布与回滚
| 检查项 | 状态 | 依据 / 缺口 | 责任方 |
| --- | --- | --- | --- |
| 版本号 + tag 冻结(`v1.0.0`) | ❌ | 尚未打 tag(规范见 §四) | 用户(合并 main 时打) |
| 回滚方案已演练 | ❌ | 方案已写(`DEPLOY.md`§七),**未演练** | 用户/运维 |
| 值班表 + 告警 Runbook 就绪 | ⚠️ | `DEPLOY.md`§九「故障速查」可作为 Runbook 初稿;**值班表未定** | 用户 |
| 灰度/放量计划明确 | ❌ | 单机 Compose 无灰度能力;生产上 K8s 后再做 | 用户 |
### 七、观察窗口(上线后)
| 检查项 | 状态 | 依据 / 缺口 | 责任方 |
| --- | --- | --- | --- |
| 核心链路监控 | ❌ | 监控未接;**上线首日建议人工每 30 分钟跑一次 `tools/smoke.sh` + 看 `docker compose logs`** 作为过渡 | 用户/运维 |
| 支付/订单对账数据回看 | ⚠️ | 对账能力已在(`/admin/reconcile/run`,`storeAmountSum` 无数据源时返回 null + notes),**真实商店数据要等凭据** | Codex/用户 |
| 崩溃率/错误率观察 | ❌ | 未接监控;容器日志可人工查 | 用户/运维 |
| 问题复盘归档 | ⚠️ | 本工程有 PROGRESS/回执留痕习惯,可沿用 | Codex/用户 |
## 九、风险 / 待确认(`CODING_STANDARDS`§7 要求:技术决策必须留痕)
| # | 风险 / 待确认 | 影响 | 建议与责任方 |
| --- | --- | --- | --- |
| R1 | **媒资仍在本机磁盘**(`./data/uploads`),未迁 OSS/CDN | 单机磁盘满/机器损坏即媒资全丢;播放无法借 CDN 扛量 | `12-上线检查清单`§四把"迁 OSS/CDN"列为上线项,**属上线前必补**;`MediaStorageService` 已是唯一替换点,切换工作量可控(Codex 可做,需 OSS 账号) |
| R2 | **单点无高可用**:一台办公室主机跑全栈 | 机器/网络故障 = 服务全停;无灰度能力 | test 阶段可接受;**生产必须按 `04-部署架构`§三 生产规格(应用 2+、DB 主备)+ §五 上 K8s** |
| R3 | **备份未演练、无 WAL 归档、备份与库同机** | 真出事时可能既恢复不了也丢得多(RPO 远大于 5 分钟) | 上线前至少"每日全量 + 异机保存 + 一次恢复演练";两周内补 WAL 归档与加密(见 §五) |
| R4 | **监控与告警未接** | 故障靠人发现;上线首日风险最高的窗口无自动告警 | 短期用"人工冒烟 + 容器 healthcheck";随后按 `07-监控告警方案`部署(需先给服务加 actuator,属代码改动) |
| R5 | **商店合规未办**(隐私政策/Data Safety/SDK 清单/版权台账/包名) | 无法上架;印尼还涉及 Kominfo PSE 注册(未注册可能被整体封锁,见 `docs/08`/PRD 合规节) | 全部为用户侧工作;其中"包名统一"建议在上架前完成(一旦上架不可改) |
| R6 | **真实 Google Play 联调未做**(缺 packageName + service account JSON) | 支付闭环在真机/商店侧未验证;`STORE_VERIFIER=disabled` 时对外明确返回"尚未配置",不会产生假订单 | 用户提供凭据后,Codex 用沙盒测试购买联调(`STORE_VERIFIER=google-play`) |
| R7 | **HTTPS/域名未就绪**,当前 IP+HTTP | 不满足 `04-部署架构`§四与 `10-安全方案`§二;公网明文传输不可接受 | 上线前按 §二 配域名 + 证书 + nginx(用户/运维) |
| R8 | **DB 账号未做最小权限**(单账号读写全库) | 应用被攻破即拥有 DDL 能力 | 拆 `fruitshorts_app`(仅 DML)与备份只读账号(见 §七) |
| R9 | **`/swagger-ui/**` 静态资源在 `SWAGGER_ENABLED=false` 时仍可访问** | 不泄露接口信息(spec 与 config 都已 404),但严格合规环境下应彻底关闭 | 可选加固:`spring.web.resources` 排除 webjar;需你点头再改(Codex) |
| R10 | 转码 worker 与在线服务**同机**(虽为独立容器) | 大批量转码会抢 CPU,影响播放稳定性 | `04-部署架构`§三 的完整方案是 worker 独立 1 台;上线前如有第二台机器就拆开,否则给 worker 容器加 CPU 限额(Codex 可改 compose) |
## 十、上线前必补清单(按优先级)
| 优先级 | 事项 | 依赖 | 责任方 |
| --- | --- | --- | --- |
| P0 | HTTPS + 独立域名 + nginx 反代(§二) | 域名、证书 | 用户/运维 |
| P0 | 每日全量备份 + 异机保存 + 一次恢复演练(§五) | 目标机 + 异地存储 | 用户/运维(Codex 可提供脚本) |
| P0 | 媒资迁 OSS/CDN(R1) | 云账号 | 用户 + Codex |
| P1 | tag 规范落地 + 回滚演练(§四) | — | 用户(合并 main 时)/ Codex 配合 |
| P1 | 监控告警最低可用(§六:容器 unhealthy 能到人) | 通知渠道 | 用户/运维 |
| P1 | 真实 Google Play 联调(R6) | 付费凭据 | 用户 → Codex |
| P2 | DB 最小权限账号(R8)、密钥集中托管(§七) | 运维 | 用户/运维 |
| P2 | K8s 生产化(R2)、灰度能力、WAL 归档与加密 | 生产资源 | 后续阶段 |