docs: freeze pilot release runbook
This commit is contained in:
parent
a8eb525bdb
commit
fe92fab4dd
11
P0-研发落地清单.md
11
P0-研发落地清单.md
@ -54,12 +54,13 @@
|
|||||||
| **A7** | **报告前后照片必填后端兜底** | 缺 before 返回 `BEFORE_PHOTO_REQUIRED`;缺 after 返回 `AFTER_PHOTO_REQUIRED` | Report Media Backend | ✅ 已实现(Batch B,含 `ReportControllerTest`) | `mvn -f backend/pom.xml test` | 与前端 B5 同步落地 |
|
| **A7** | **报告前后照片必填后端兜底** | 缺 before 返回 `BEFORE_PHOTO_REQUIRED`;缺 after 返回 `AFTER_PHOTO_REQUIRED` | Report Media Backend | ✅ 已实现(Batch B,含 `ReportControllerTest`) | `mvn -f backend/pom.xml test` | 与前端 B5 同步落地 |
|
||||||
| **A8** | **统一鉴权上下文**:HMAC session token + `CurrentUserContext`,protected API 不再信任请求里的身份字段 | 未登录返回 401;A 店用户访问 B 店预约/报告/线索返回 403;customer 不能用任意 `userId` 创建他人宠物/预约 | System Architect / Backend Core | ✅ 已实现(Batch C,含 `SessionTokenServiceTest`) | `mvn -f backend/pom.xml test` + 跨店/跨用户接口验证 | 对应 P0-1;不引入完整 Spring Security |
|
| **A8** | **统一鉴权上下文**:HMAC session token + `CurrentUserContext`,protected API 不再信任请求里的身份字段 | 未登录返回 401;A 店用户访问 B 店预约/报告/线索返回 403;customer 不能用任意 `userId` 创建他人宠物/预约 | System Architect / Backend Core | ✅ 已实现(Batch C,含 `SessionTokenServiceTest`) | `mvn -f backend/pom.xml test` + 跨店/跨用户接口验证 | 对应 P0-1;不引入完整 Spring Security |
|
||||||
| **A9** | **线索脱敏**:`/report/leads` 默认不返回完整 `wechatOpenid/wechatUnionid` | 回包只有 `wechatBound`、`wechatOpenidMasked`、`wechatUnionidMasked` | Report Media Backend | ✅ 已实现(Batch B) | 手工 curl + `mvn test` | 对应 P0-2 |
|
| **A9** | **线索脱敏**:`/report/leads` 默认不返回完整 `wechatOpenid/wechatUnionid` | 回包只有 `wechatBound`、`wechatOpenidMasked`、`wechatUnionidMasked` | Report Media Backend | ✅ 已实现(Batch B) | 手工 curl + `mvn test` | 对应 P0-2 |
|
||||||
| **A10** | **配置安全与启动迁移收口**:`application.yml` 不保留真实敏感值;生产 profile 禁用 `ddl-auto:update`/`show-sql`;移除启动期 DDL/历史 UPDATE | 仓库不含真实 DB/微信密钥;生产启动只读环境变量 | Backend Ops / System Architect | ✅ 已实现(Batch D) | `rg` 扫描 + 启动验证 | 对应 P0-8;已暴露凭据需轮换 |
|
| **A10** | **配置安全与启动迁移收口**:`application.yml` 不保留真实敏感值;生产 profile 禁用 `ddl-auto:update`/`show-sql`、万能验证码和宽 CORS;生产启动不写默认业务数据 | 仓库不含真实 DB/微信密钥;最终配置不安全时启动失败;有 liveness/readiness、只读 schema/数据预检和上线 smoke | Backend Ops / System Architect | ✅ 代码与脚本已实现;待真实环境留证 | `mvn test` + `deploy/release-preflight.sh` + `deploy/production-smoke.sh` | 对应 P0-8;生产域名/凭据/迁移/线上 smoke 归 H3;已暴露凭据需轮换 |
|
||||||
| **A11** | **上传路径与类型保护**:`FileController` 归一化路径 + 扩展名/MIME 校验 | `/api/upload/image/../...`、伪装扩展名、超大文件均被拒绝 | Backend Core / System Architect | ✅ 已实现(Batch D) | 手工 curl + `mvn test` | 对应 P0-9 |
|
| **A11** | **上传路径与类型保护**:`FileController` 归一化路径 + 扩展名/MIME 校验 | `/api/upload/image/../...`、伪装扩展名、超大文件均被拒绝 | Backend Core / System Architect | ✅ 已实现(Batch D) | 手工 curl + `mvn test` | 对应 P0-9 |
|
||||||
| **A12** | **最小测试基线**:补 `SessionTokenServiceTest`、`ReportServiceTest`、`ReportControllerTest`、`AppointmentServiceTest` | `mvn test` 不再输出 `No tests to run`,新增用例全部通过 | Core Flow QA / System Architect | ✅ 已实现(Batch D) | `mvn -f backend/pom.xml test` | 对应 P0-10;持续回归数量以 Surefire 当次统计为准 |
|
| **A12** | **最小测试基线**:补 `SessionTokenServiceTest`、`ReportServiceTest`、`ReportControllerTest`、`AppointmentServiceTest` | `mvn test` 不再输出 `No tests to run`,新增用例全部通过 | Core Flow QA / System Architect | ✅ 已实现(Batch D) | `mvn -f backend/pom.xml test` | 对应 P0-10;持续回归数量以 Surefire 当次统计为准 |
|
||||||
| **A13** | **预约/报告身份语义拆分**:客户、创建人、服务技师、报告作者不得复用同一语义字段 | customer 自约身份从 session 派生;门店代约必须提供真实宠主;宠主报告按预约客户归属查询;legacy 字段只做兼容 | System Architect / Backend Core / Report Media Backend | ✅ 已实现(Phase 0 RC1 后) | `mvn -f backend/pom.xml test` + H5/小程序构建 | 迁移见 `backend/db/migrations/20260801_split_service_identity.sql` |
|
| **A13** | **预约/报告身份语义拆分**:客户、创建人、服务技师、报告作者不得复用同一语义字段 | customer 自约身份从 session 派生;门店代约必须提供真实宠主;宠主报告按预约客户归属查询;legacy 字段只做兼容 | System Architect / Backend Core / Report Media Backend | ✅ 已实现(Phase 0 RC1 后) | `mvn -f backend/pom.xml test` + H5/小程序构建 | 迁移见 `backend/db/migrations/20260801_split_service_identity.sql` |
|
||||||
| **A14** | **StoreCustomer 门店客户主档**:客户列表不再临时拼成无 ID 投影 | 预约/留资自动建档;同店账号与手机号唯一;API 返回 storeCustomerId 且手机脱敏;历史数据可回填 | Backend Core / Report Media Backend / Data Model | ✅ 已实现(待生产迁移) | `mvn -f backend/pom.xml test` + `npm --prefix admin run build` | 迁移见 `backend/db/migrations/20260801_create_store_customer.sql` |
|
| **A14** | **StoreCustomer 门店客户主档**:客户列表不再临时拼成无 ID 投影 | 预约/留资自动建档;同店账号与手机号唯一;API 返回 storeCustomerId 且手机脱敏;历史数据可回填 | Backend Core / Report Media Backend / Data Model | ✅ 已实现(待生产迁移) | `mvn -f backend/pom.xml test` + `npm --prefix admin run build` | 迁移见 `backend/db/migrations/20260801_create_store_customer.sql` |
|
||||||
| **A15** | **BusinessEvent 业务事件落库**:经营漏斗不再依赖日志或前端猜测 | 首批 7 类事实统一落库;稳定事件幂等;metadata 不含 token/手机号/IP;工作台已打开可统计;历史可靠事实可回填 | Backend Core / Report Media Backend / Data Model | ✅ 已实现(待生产迁移) | `mvn -f backend/pom.xml test` + `npm --prefix admin run build` + 本体漂移审计 | 迁移见 `backend/db/migrations/20260801_create_business_event.sql` |
|
| **A15** | **BusinessEvent 业务事件落库**:经营漏斗不再依赖日志或前端猜测 | 首批 7 类事实统一落库;稳定事件幂等;metadata 不含 token/手机号/IP;工作台已打开可统计;历史可靠事实可回填 | Backend Core / Report Media Backend / Data Model | ✅ 已实现(待生产迁移) | `mvn -f backend/pom.xml test` + `npm --prefix admin run build` + 本体漂移审计 | 迁移见 `backend/db/migrations/20260801_create_business_event.sql` |
|
||||||
|
| **A16** | **真实预约容量与连续排班占用**:服务时长、门店并发容量和预约时长快照共同决定号源 | 长服务跨半小时桶占号;`walk_in` 消耗一份容量,`blocked` 关闭全部;并发创建不超卖 | Backend Core / Store Admin FE / Data Model | ✅ 已实现(待生产迁移) | backend 全测 + admin/H5/小程序构建 + 本体漂移审计 | 迁移见 `backend/db/migrations/20260801_create_booking_capacity.sql`;不等同排队/准点承诺 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -130,8 +131,8 @@
|
|||||||
|
|
||||||
| ID | 需求 | 验收标准 | Owner | 状态 | 验收命令 | 备注 |
|
| ID | 需求 | 验收标准 | Owner | 状态 | 验收命令 | 备注 |
|
||||||
|----|------|----------|-------|------|----------|------|
|
|----|------|----------|-------|------|----------|------|
|
||||||
| **H1** | **backend README RC 清单** | 含 `ffmpeg -version`、`ffprobe -version`、`curl -I $APP_BASE_URL/api/upload/image/<known-file>`、`mvn test`;写明 `APP_BASE_URL`、`HIGHLIGHT_*`、`PETSTORE_SESSION_SECRET`、上传目录权限、Nginx `client_max_body_size` | Backend Ops | ✅ 已实现(Batch D) | 代码审查 | 对应 P0-10 |
|
| **H1** | **后端可重复发布门禁** | README + production profile + 配置校验 + schema/15 项只读预检 + liveness/readiness + 只读 smoke + systemd/Nginx 模板 | Backend Ops | ✅ 仓库能力已实现;待真实环境执行 | backend 全测/打包 + `bash -n deploy/*.sh` + Runbook | 不把模板/本地通过写成已上线 |
|
||||||
| **H2** | **frontend README RC 清单** | 含 `npm install`、`build:h5`、`build:mp-weixin`;写明 `VITE_API_ORIGIN`、`VITE_REPORT_PUBLIC_ORIGIN`、微信 request/download 合法域名、报告二维码链接检查 | Frontend Release Ops | ✅ 已实现(Batch D) | 代码审查 | 对应 P0-10 |
|
| **H2** | **前端可重复发布门禁** | admin/H5/小程序真实域名配置先门禁再构建;仓库占位值不能进入正式包;微信合法域名、体验版、静态回滚路径明确 | Frontend Release Ops | ✅ 仓库能力已实现;待真实环境执行 | admin/frontend preflight + 三端构建 + Runbook | 真实配置不得提交 |
|
||||||
| **H3** | **P0 通过条件** | 见下方「P0 通过条件」块 | System Architect / Core Flow QA | 🟡 待人工 smoke 复核 | 见下方 | 对应 P0-10 |
|
| **H3** | **P0 通过条件** | 见下方「P0 通过条件」块 | System Architect / Core Flow QA | 🟡 待人工 smoke 复核 | 见下方 | 对应 P0-10 |
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -158,7 +159,9 @@
|
|||||||
- 同一 `appointmentId` 第二次提交报告返回 `REPORT_ALREADY_EXISTS`。
|
- 同一 `appointmentId` 第二次提交报告返回 `REPORT_ALREADY_EXISTS`。
|
||||||
- 报告前后照片缺失返回对应业务码。
|
- 报告前后照片缺失返回对应业务码。
|
||||||
- A 店 boss/staff 不能查 B 店 leads/list/report;customer 不能用任意 `userId` 创建他人宠物/预约。
|
- A 店 boss/staff 不能查 B 店 leads/list/report;customer 不能用任意 `userId` 创建他人宠物/预约。
|
||||||
- 生产配置扫描不含真实 DB/微信密钥,无 `ddl-auto:update`,无宽 CORS。
|
- 生产配置扫描不含真实 DB/微信密钥,最终 profile 为 `ddl-auto=validate`,CORS 仅允许显式生产 HTTPS 源;只读生产预检通过。
|
||||||
|
- 已按顺序完成身份、StoreCustomer、BusinessEvent、预约容量四个生产迁移,并归档备份与验证证据。
|
||||||
|
- liveness/readiness、只读 smoke 与 boss/staff/customer 三角色真实环境 smoke 通过;Gitea/GitLab 未受 Petstore 配置影响。
|
||||||
- 文档口径扫描:`rg "到店不排队|会员储值 \| 路线图 P0|宠主通过微信查看服务报告(v2)|无限制,随便选" docs` 无命中。
|
- 文档口径扫描:`rg "到店不排队|会员储值 \| 路线图 P0|宠主通过微信查看服务报告(v2)|无限制,随便选" docs` 无命中。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@ -87,7 +87,8 @@ python3 docs/graph/audit_drift.py
|
|||||||
| BR-SCH-001 / BR-ST-001 / BR-PET-001 | 对应 ControllerTest | anchored |
|
| BR-SCH-001 / BR-ST-001 / BR-PET-001 | 对应 ControllerTest | anchored |
|
||||||
| BR-USER-001/002 | 间接 | anchored |
|
| BR-USER-001/002 | 间接 | anchored |
|
||||||
| BR-USER-003 敏感字段 JsonIgnore | UserJsonIgnoreTest | anchored |
|
| BR-USER-003 敏感字段 JsonIgnore | UserJsonIgnoreTest | anchored |
|
||||||
| BR-CONFIG-001/002 | —(配置) | anchored |
|
| BR-CONFIG-001/002 | ProductionConfigurationValidatorTest(002) | anchored |
|
||||||
|
| BR-CONFIG-003 生产发布只读门禁 | 配置单测 + release/smoke 脚本;live 证据待发布 | anchored |
|
||||||
| BR-TST-001 一报告一寄语 | ReportTestimonialServiceTest | anchored |
|
| BR-TST-001 一报告一寄语 | ReportTestimonialServiceTest | anchored |
|
||||||
| BR-UNSUB-001 / BR-INT-001 | ReportLeadControllerTest | anchored |
|
| BR-UNSUB-001 / BR-INT-001 | ReportLeadControllerTest | anchored |
|
||||||
| BR-SC-001 / BR-BE-001 | StoreCustomerServiceTest / BusinessEventServiceTest / MigrationTest | anchored |
|
| BR-SC-001 / BR-BE-001 | StoreCustomerServiceTest / BusinessEventServiceTest / MigrationTest | anchored |
|
||||||
@ -104,7 +105,7 @@ python3 docs/graph/audit_drift.py
|
|||||||
| ~~中~~ | ~~`rule:BR-STORE-003` 公开 Store 字段裁剪~~ | ✅ `toPublicStoreView` |
|
| ~~中~~ | ~~`rule:BR-STORE-003` 公开 Store 字段裁剪~~ | ✅ `toPublicStoreView` |
|
||||||
| 中 | `uk_report_appointment` 上线迁移 | 🟡 运维 |
|
| 中 | `uk_report_appointment` 上线迁移 | 🟡 运维 |
|
||||||
| ~~中~~ | ~~预约/履约/报告打开/留资事件可统计落地~~ | ✅ BusinessEvent;成片/播放/保存事件另行接入 |
|
| ~~中~~ | ~~预约/履约/报告打开/留资事件可统计落地~~ | ✅ BusinessEvent;成片/播放/保存事件另行接入 |
|
||||||
| 中 | production profile 实测启动 | 🟡 |
|
| 中 | production profile + 只读预检在真实生产配置实测 | 🟡 发布门禁 |
|
||||||
| 低 | 成片真 FFmpeg 端到端集成测(本机有 ffmpeg) | 🟡 单测已覆盖状态机与归类 |
|
| 低 | 成片真 FFmpeg 端到端集成测(本机有 ffmpeg) | 🟡 单测已覆盖状态机与归类 |
|
||||||
| 低 | `action:logout` / `event:session_revoked` 服务端吊销 | 🟡 gap |
|
| 低 | `action:logout` / `event:session_revoked` 服务端吊销 | 🟡 gap |
|
||||||
| 低 | 废弃 `register_store` / wechat OAuth demo 代码路径 | 🟡 deprecated 已标 |
|
| 低 | 废弃 `register_store` / wechat OAuth demo 代码路径 | 🟡 deprecated 已标 |
|
||||||
|
|||||||
@ -134,7 +134,8 @@
|
|||||||
{"id":"rule:BR-USER-001","type":"Rule","name":"员工管理仅 boss + 同店","domain":"user","appliesTo":["action:create_staff","action:list_staff","action:delete_staff"],"code":"backend/src/main/java/com/petstore/controller/UserController.java","evidence":"anchored"}
|
{"id":"rule:BR-USER-001","type":"Rule","name":"员工管理仅 boss + 同店","domain":"user","appliesTo":["action:create_staff","action:list_staff","action:delete_staff"],"code":"backend/src/main/java/com/petstore/controller/UserController.java","evidence":"anchored"}
|
||||||
{"id":"rule:BR-USER-002","type":"Rule","name":"用户更新仅本人","domain":"user","appliesTo":["action:update_user","action:get_user_info"],"code":"backend/src/main/java/com/petstore/controller/UserController.java#updateUser; #info","evidence":"anchored"}
|
{"id":"rule:BR-USER-002","type":"Rule","name":"用户更新仅本人","domain":"user","appliesTo":["action:update_user","action:get_user_info"],"code":"backend/src/main/java/com/petstore/controller/UserController.java#updateUser; #info","evidence":"anchored"}
|
||||||
{"id":"rule:BR-CONFIG-001","type":"Rule","name":"生产配置环境变量","domain":"config","appliesTo":[],"code":"backend/src/main/resources/application.yml; application-example.yml","doc":"backend/README.md 环境变量表","evidence":"anchored"}
|
{"id":"rule:BR-CONFIG-001","type":"Rule","name":"生产配置环境变量","domain":"config","appliesTo":[],"code":"backend/src/main/resources/application.yml; application-example.yml","doc":"backend/README.md 环境变量表","evidence":"anchored"}
|
||||||
{"id":"rule:BR-CONFIG-002","type":"Rule","name":"production profile:ddl=validate + show-sql=false + SMS 禁用","domain":"config","appliesTo":[],"code":"backend/src/main/resources/application.yml production profile","doc":"backend/README.md production profile","evidence":"anchored"}
|
{"id":"rule:BR-CONFIG-002","type":"Rule","name":"production profile 强制只读 schema 安全配置","domain":"config","appliesTo":[],"code":"backend/src/main/resources/application.yml production profile; backend/src/main/java/com/petstore/config/ProductionConfigurationValidator.java; backend/src/main/java/com/petstore/PetstoreApplication.java","doc":"backend/README.md production profile","test":"ProductionConfigurationValidatorTest","evidence":"anchored","note":"生产禁用自动建表/show-sql/SMS 万能码/默认数据初始化,并拒绝弱密钥、占位域名和宽 CORS"}
|
||||||
|
{"id":"rule:BR-CONFIG-003","type":"Rule","name":"生产发布只读门禁","domain":"config","appliesTo":[],"code":"backend/src/main/java/com/petstore/config/ProductionDatabasePreflightRunner.java; backend/src/main/java/com/petstore/config/PetstoreRuntimeHealthIndicator.java; backend/deploy/release-preflight.sh; backend/deploy/production-smoke.sh","doc":"docs/生产发布与回滚Runbook-2026-08-01.md; docs/生产监控与告警基线-2026-08-01.md","evidence":"anchored","note":"备份和四迁移后运行 schema validate + 15 项只读不变量;readiness 覆盖 DB/磁盘/上传/FFmpeg;自动 smoke 只读"}
|
||||||
{"id":"rel:user_owns_pets","type":"Relation","subject":"entity:user","predicate":"OWNS","object":"entity:pet","cardinality":"1—N","evidence":"anchored","doc":"Pet.owner_user_id → User.id","domain":"user"}
|
{"id":"rel:user_owns_pets","type":"Relation","subject":"entity:user","predicate":"OWNS","object":"entity:pet","cardinality":"1—N","evidence":"anchored","doc":"Pet.owner_user_id → User.id","domain":"user"}
|
||||||
{"id":"rel:pet_has_appointments","type":"Relation","subject":"entity:pet","predicate":"HAS","object":"entity:appointment","cardinality":"1—N","evidence":"anchored","doc":"Appointment.pet_id(可空)","domain":"pet"}
|
{"id":"rel:pet_has_appointments","type":"Relation","subject":"entity:pet","predicate":"HAS","object":"entity:appointment","cardinality":"1—N","evidence":"anchored","doc":"Appointment.pet_id(可空)","domain":"pet"}
|
||||||
{"id":"rel:store_has_service_types","type":"Relation","subject":"entity:store","predicate":"HAS","object":"entity:service_type","cardinality":"1—N","evidence":"anchored","doc":"ServiceType.store_id(NULL=系统默认)","domain":"store"}
|
{"id":"rel:store_has_service_types","type":"Relation","subject":"entity:store","predicate":"HAS","object":"entity:service_type","cardinality":"1—N","evidence":"anchored","doc":"ServiceType.store_id(NULL=系统默认)","domain":"store"}
|
||||||
|
|||||||
@ -123,6 +123,7 @@
|
|||||||
| `rule:BR-INT-001` | `action:get_report_suggestion` |
|
| `rule:BR-INT-001` | `action:get_report_suggestion` |
|
||||||
| `rule:BR-CONFIG-001` | —(全局约束) |
|
| `rule:BR-CONFIG-001` | —(全局约束) |
|
||||||
| `rule:BR-CONFIG-002` | —(全局约束) |
|
| `rule:BR-CONFIG-002` | —(全局约束) |
|
||||||
|
| `rule:BR-CONFIG-003` | —(发布全局约束) |
|
||||||
|
|
||||||
## 实体—归属系统(续)
|
## 实体—归属系统(续)
|
||||||
|
|
||||||
|
|||||||
@ -306,12 +306,22 @@
|
|||||||
|
|
||||||
### rule:BR-CONFIG-002
|
### rule:BR-CONFIG-002
|
||||||
|
|
||||||
**production profile**:`--spring.profiles.active=production` 时 `ddl-auto=validate`(不改表)、`show-sql=false`、`SMS_UNIVERSAL_CODE` 默认空(关闭万能验证码 `rule:BR-AUTH-002`)。
|
**production profile**:`--spring.profiles.active=production` 时 `ddl-auto=validate`(不改表)、`show-sql=false`、`SMS_UNIVERSAL_CODE` 默认空(关闭万能验证码 `rule:BR-AUTH-002`),并跳过开发期默认服务初始化。最终配置不满足强密钥、显式生产 HTTPS 域名、绝对上传目录和显式 CORS 源时启动失败。
|
||||||
|
|
||||||
- **适用动作**:—(全局约束)
|
- **适用动作**:—(全局约束)
|
||||||
- **代码**:`backend/src/main/resources/application.yml`(multi-document production profile)
|
- **代码**:`backend/src/main/resources/application.yml`(multi-document production profile)、`ProductionConfigurationValidator.java`、`PetstoreApplication.java`
|
||||||
|
- **测试**:`ProductionConfigurationValidatorTest`
|
||||||
- **文档**:`backend/README.md production profile`
|
- **文档**:`backend/README.md production profile`
|
||||||
- **证据**:`anchored`(配置已加;production 实测启动 `gap`)
|
- **证据**:`anchored`(真实生产值与启动证据仍属发布门禁)
|
||||||
|
|
||||||
|
### rule:BR-CONFIG-003
|
||||||
|
|
||||||
|
**生产发布只读门禁**:正式切流前必须按固定顺序完成备份和四个版本化迁移,再以最终生产配置运行 JPA schema `validate` 与 15 项只读数据不变量检查。运行期 readiness 必须同时覆盖数据库、磁盘、上传目录和 FFmpeg/ffprobe;上线自动 smoke 只允许 GET,不创建或修改业务数据。
|
||||||
|
|
||||||
|
- **适用动作**:—(发布全局约束)
|
||||||
|
- **代码**:`backend/src/main/java/com/petstore/config/ProductionDatabasePreflightRunner.java`、`PetstoreRuntimeHealthIndicator.java`、`backend/deploy/release-preflight.sh`、`production-smoke.sh`
|
||||||
|
- **文档**:`docs/生产发布与回滚Runbook-2026-08-01.md`、`docs/生产监控与告警基线-2026-08-01.md`
|
||||||
|
- **证据**:`anchored`(生产备份、迁移和 live smoke 证据待发布时补齐)
|
||||||
|
|
||||||
## 寄语 / 退订 / 建议周期
|
## 寄语 / 退订 / 建议周期
|
||||||
|
|
||||||
|
|||||||
106
qa-reports/phase0-pilot-rc2-release-manifest.md
Normal file
106
qa-reports/phase0-pilot-rc2-release-manifest.md
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
# 宠小它 Phase 0 Pilot RC2 Release Manifest
|
||||||
|
|
||||||
|
> RC ID:`phase0-pilot-rc2`
|
||||||
|
>
|
||||||
|
> 冻结日期:2026-08-01
|
||||||
|
>
|
||||||
|
> 状态:**Ready for Release(未部署)**
|
||||||
|
>
|
||||||
|
> RC Steward:PM Assistant
|
||||||
|
>
|
||||||
|
> 发布边界:本 manifest 只冻结源码与本地验证证据;真实生产配置、备份、迁移、TLS、live smoke 和三角色验收未完成前不得改为 `Released`。
|
||||||
|
|
||||||
|
## 1. 冻结提交与回滚点
|
||||||
|
|
||||||
|
| 仓库 | RC commit | 前一稳定 commit(应用回滚点) | 状态 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| backend | `133f61e4ad6152865a76c4e9328d96e689f53635` | `b6138475f073881199abdef8d2681dcb92d1ab66` | clean |
|
||||||
|
| admin | `3470b124e408501ed824b993bcb34dd40e77c293` | `7e924fa79d6dafbd60643b8e31519737feb92e3b` | clean |
|
||||||
|
| frontend | `e11ce1aaaa602011718e300e4527a0d971f1339a` | `d29ece3a85e6d137d91afd609f49b10051ad9b7f` | clean |
|
||||||
|
| docs | 本 manifest 所在 commit | 前一 commit | 提交后确认 clean |
|
||||||
|
|
||||||
|
RC 只能部署以上 commit 或由其生成且校验和一致的产物。后续任何代码改动必须建立新 RC 或 Hotfix manifest。
|
||||||
|
|
||||||
|
## 2. 纳入范围
|
||||||
|
|
||||||
|
- 生产 profile 启动配置门禁:`ddl-auto=validate`、关闭 SQL/万能验证码、强 session secret、显式 HTTPS 域名、显式 CORS、绝对上传目录;
|
||||||
|
- 生产启动跳过开发期默认服务写入;
|
||||||
|
- JPA schema 校验后执行 15 项只读数据库不变量预检;
|
||||||
|
- liveness/readiness,readiness 覆盖 DB、磁盘、上传目录、FFmpeg/ffprobe;
|
||||||
|
- 后端只读 smoke、systemd/EnvironmentFile/Nginx 独立模板;
|
||||||
|
- admin、H5、小程序发布域名门禁,默认 `.invalid` 占位配置主动失败;
|
||||||
|
- 小程序构建显式使用 `--mode mp-weixin`,读取独立环境配置;
|
||||||
|
- 生产 Runbook、试点门店开通清单、监控告警基线和本体 `BR-CONFIG-003`;
|
||||||
|
- 已冻结的身份拆分、StoreCustomer、BusinessEvent、真实预约容量能力及四个迁移脚本。
|
||||||
|
|
||||||
|
## 3. 明确排除
|
||||||
|
|
||||||
|
- 未连接、未迁移、未修改任何生产数据库;
|
||||||
|
- 未安装 systemd unit,未改服务器 Nginx,未申请或续签 TLS;
|
||||||
|
- 未重启 Petstore、Gitea、GitLab 或其他服务;
|
||||||
|
- 未使用真实生产域名、微信账号、AppSecret、密码或 token;
|
||||||
|
- 未上传微信体验版/正式版,未完成真机或生产三角色 smoke;
|
||||||
|
- 未接入真实外部监控和告警通知渠道;
|
||||||
|
- 不包含支付、库存、储值、复杂会员、多门店组织、订单系统或泛 AI。
|
||||||
|
|
||||||
|
## 4. 本地验证证据
|
||||||
|
|
||||||
|
环境:
|
||||||
|
|
||||||
|
- OpenJDK `21.0.10`(源码按 Java 17 release 编译)
|
||||||
|
- Maven `3.9.14`
|
||||||
|
- Node `v22.22.2`
|
||||||
|
- npm `10.9.7`
|
||||||
|
|
||||||
|
结果:
|
||||||
|
|
||||||
|
| 门禁 | 结果 |
|
||||||
|
|---|---|
|
||||||
|
| `mvn test` | 158 tests,0 failure / 0 error / 0 skipped |
|
||||||
|
| `mvn -DskipTests package` | BUILD SUCCESS |
|
||||||
|
| backend shell `bash -n` | PASS |
|
||||||
|
| backend/admin/frontend `git diff --check` | PASS |
|
||||||
|
| admin `npm run build` | PASS;存在既有大 chunk warning,不阻塞功能验证 |
|
||||||
|
| frontend `npm run build:h5` | PASS |
|
||||||
|
| frontend `npm run build:mp-weixin` | PASS;`verify-mp-app-json` PASS |
|
||||||
|
| admin/frontend 默认占位配置门禁 | 按预期失败 |
|
||||||
|
| admin/frontend 受控测试 HTTPS 源门禁 | PASS;输出只含变量名,不含值 |
|
||||||
|
| `validate_ontology.py` | 238 条引用完整 |
|
||||||
|
| `audit_drift.py` | 55 endpoints、13 JPA entities,code-only / ontology-only 均为 0 |
|
||||||
|
|
||||||
|
## 5. 本地构建校验和
|
||||||
|
|
||||||
|
| 产物 | SHA-256 | 可部署性 |
|
||||||
|
|---|---|---|
|
||||||
|
| `backend/target/petstore-backend-1.0.0.jar` | `e4ef2477c549c77c7bd0ed392d772c2abd62fb5fdf2e932124cd82726e7d01d2` | 可作为候选 jar;仍须最终生产环境预检 |
|
||||||
|
| admin `dist` 文件哈希清单的聚合 SHA-256 | `017089e4c7fd236b6efcbdcba10b32b9e423e3ccef9613e7ea66717b65b2e984` | **不可部署**:使用仓库 `.invalid` 占位配置构建,仅证明可编译 |
|
||||||
|
| H5 `dist/build/h5` 聚合 SHA-256 | `d816be92a12f353b2edb6f19987936f7f25e1b09d0a285c4203da494caeb123e` | **不可部署**:使用仓库 `.invalid` 占位配置构建,仅证明可编译 |
|
||||||
|
| 小程序 `dist/build/mp-weixin` 聚合 SHA-256 | `97489bb9b4b93f96606a10459b6d73526c8287d598019c1651490be854376084` | **不可上传**:使用仓库 `.invalid` 占位配置构建,仅证明可编译 |
|
||||||
|
|
||||||
|
正式 admin/H5/小程序必须用权限受控的真实域名文件重新运行 `preflight:release` 和构建,并把新产物校验和追加到生产发布记录。
|
||||||
|
|
||||||
|
## 6. 生产 Go/No-Go 门禁
|
||||||
|
|
||||||
|
- [ ] Petstore 专用 API/admin/report 域名与 TLS 已就绪,不复用其他产品域名;
|
||||||
|
- [ ] 最终配置通过后端、admin、frontend release preflight,未输出任何秘密值;
|
||||||
|
- [ ] 生产数据库完成全量备份、SHA-256 和隔离恢复演练;
|
||||||
|
- [ ] 按顺序执行身份拆分、StoreCustomer、BusinessEvent、预约容量四个迁移,末尾检查均为 0;
|
||||||
|
- [ ] 后端 15 项只读不变量预检与 JPA `validate` 通过;
|
||||||
|
- [ ] Petstore systemd/Nginx 配置独立,`nginx -t` 通过;
|
||||||
|
- [ ] liveness/readiness 和只读 smoke 通过;
|
||||||
|
- [ ] boss/staff/customer 三角色人工 smoke 及权限/异常反例通过;
|
||||||
|
- [ ] 微信合法域名、体验版和真机验证通过;
|
||||||
|
- [ ] 外部监控、告警联系人和值班窗口真实测试通过;
|
||||||
|
- [ ] Gitea/GitLab 健康回归通过,配置 diff 证明未被改动;
|
||||||
|
- [ ] 前一应用/静态/小程序回滚点明确,数据库不做自动 down migration。
|
||||||
|
|
||||||
|
任一项未勾选,结论均为 `No-Go / Ready for Release`,不得写成已上线。
|
||||||
|
|
||||||
|
## 7. 风险与回滚
|
||||||
|
|
||||||
|
- admin 主 bundle 仍有大于 500 kB 的构建 warning;试点流量下不是上线阻断,但应进入后续性能债,不与本次发布安全闭环混排。
|
||||||
|
- readiness 会把上传目录或 FFmpeg 依赖异常视为不可接流;服务器权限和二进制路径必须在切流前验证。
|
||||||
|
- 四个迁移为前向兼容追加式变更。常规故障只回退应用和静态软链接,不删除列/表/索引;灾难性数据恢复需要独立授权和变更单。
|
||||||
|
- 微信小程序必须走平台版本回退,不能依靠服务器软链接。
|
||||||
|
|
||||||
|
详细步骤见《生产发布与回滚Runbook-2026-08-01.md》;试点运营和指标口径见《试点门店开通清单-2026-08-01.md》;告警见《生产监控与告警基线-2026-08-01.md》。
|
||||||
224
生产发布与回滚Runbook-2026-08-01.md
Normal file
224
生产发布与回滚Runbook-2026-08-01.md
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
# 宠小它生产发布与回滚 Runbook
|
||||||
|
|
||||||
|
> 版本:2026-08-01
|
||||||
|
>
|
||||||
|
> 适用范围:3~5 家独立宠物洗护单店试点;backend、admin、H5、微信小程序。
|
||||||
|
>
|
||||||
|
> 当前结论:仓库交付到 `Ready for Release`,未取得真实生产域名、凭据、备份和线上验收证据前,不得标记 `Released`。
|
||||||
|
|
||||||
|
## 1. 发布原则
|
||||||
|
|
||||||
|
1. 发布单元必须冻结为四元组:`backend commit + admin commit + frontend commit + docs/RC manifest commit`,同时记录 jar/静态包校验和。
|
||||||
|
2. 生产数据库只通过已审核 SQL 迁移;应用固定 `ddl-auto=validate`,禁止启动期改表或修复数据。
|
||||||
|
3. 后端预检和自动 smoke 均为只读;创建预约、开始服务、提交报告等写路径只在明确指定的试点门店和人工验收窗口执行。
|
||||||
|
4. 应用、静态站点采用版本目录加 `current` 软链接切换;数据库迁移为向前兼容的追加式变更,不做自动向下迁移。
|
||||||
|
5. Petstore 使用独立域名、Nginx `server` 块、systemd unit 和发布目录。不得覆盖、重启或复用同机 Gitea/GitLab 的配置与服务。
|
||||||
|
6. 任何命令输出都不得包含数据库密码、微信 AppSecret、session secret、完整报告 token、手机号或私密媒体 URL。
|
||||||
|
|
||||||
|
## 2. 本次发布任务卡
|
||||||
|
|
||||||
|
| 字段 | 值 |
|
||||||
|
|---|---|
|
||||||
|
| Task Name | 建立可重复的生产发布与回滚闭环 |
|
||||||
|
| Role | Backend Ops / Frontend Release Ops |
|
||||||
|
| Owner Agent | Backend Ops(后端与 Nginx)/ Frontend Release Ops(admin、H5、小程序) |
|
||||||
|
| Paired QA | Core Flow QA / Report Share QA |
|
||||||
|
| Workstream | Release |
|
||||||
|
| Track | Ops |
|
||||||
|
| Status | Ready for Release(生产证据待补) |
|
||||||
|
| Priority | P0 |
|
||||||
|
| Repo/Path | `backend/deploy/**`、前端发布配置、`docs/**` |
|
||||||
|
| Service Lock | Petstore backend unit + Petstore 专用 Nginx server blocks + Petstore static roots |
|
||||||
|
| Depends On | 冻结 RC、真实域名/TLS、备份、四个迁移、微信合法域名 |
|
||||||
|
| Acceptance | 预检、构建、readiness、只读 smoke、三角色人工 smoke、回滚演练均有证据 |
|
||||||
|
| RC Included | Yes |
|
||||||
|
| Due Date | TBD |
|
||||||
|
| Output Link | 当前 Runbook + RC manifest |
|
||||||
|
| Blocker Owner | 生产环境负责人(域名/凭据/变更窗口) |
|
||||||
|
| Commit / RC | Not Frozen,提交后写入 RC manifest |
|
||||||
|
| Architecture Review Required | Yes |
|
||||||
|
| Data Model Review Required | Yes(迁移顺序与回滚边界) |
|
||||||
|
| Access / Privacy Review Required | Yes |
|
||||||
|
|
||||||
|
## 3. 立即停止条件
|
||||||
|
|
||||||
|
出现任一情况立即停止,不继续切流:
|
||||||
|
|
||||||
|
- 四仓 commit、构建产物或校验和与 RC manifest 不一致;
|
||||||
|
- 生产域名仍为 localhost、`.invalid`、`api.s-good.com` 或 `www.s-good.com` 等非本项目域名;
|
||||||
|
- 数据库备份未完成、校验和缺失或未在隔离库验证可恢复;
|
||||||
|
- 任一迁移末尾验证查询非 0,或预检的 15 项只读不变量非 0;
|
||||||
|
- `nginx -t` 失败,或 diff 显示将修改 Gitea/GitLab 的 server block;
|
||||||
|
- readiness 非 `UP`,上传目录/FFmpeg/数据库任一依赖失败;
|
||||||
|
- 发现凭据、完整 token、手机号或私密媒体地址进入日志/工单/截图;
|
||||||
|
- 无明确值班人、回滚执行人或生产变更授权。
|
||||||
|
|
||||||
|
## 4. 发布前冻结
|
||||||
|
|
||||||
|
RC Steward 在 `docs/qa-reports` 创建当次 manifest,至少记录:
|
||||||
|
|
||||||
|
- 四仓 commit、分支、`git status --short`(必须为空);
|
||||||
|
- Java/Node/npm/Maven 版本;
|
||||||
|
- jar、admin `dist`、H5 `dist`、小程序构建目录的 SHA-256;
|
||||||
|
- 目标环境、变更窗口、操作者、审核人;
|
||||||
|
- 本次包含/排除项、已知风险、旧版本回滚点;
|
||||||
|
- 域名只记 host,不记 query/token;配置只记变量名和“已配置”,不记值。
|
||||||
|
|
||||||
|
冻结后重新运行本地门禁:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/apple/_src/petstore/backend
|
||||||
|
mvn test
|
||||||
|
mvn -DskipTests package
|
||||||
|
bash -n deploy/release-preflight.sh deploy/production-smoke.sh
|
||||||
|
|
||||||
|
cd /Users/apple/_src/petstore/admin
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
cd /Users/apple/_src/petstore/frontend
|
||||||
|
npm run build:h5
|
||||||
|
npm run build:mp-weixin
|
||||||
|
|
||||||
|
cd /Users/apple/_src/petstore
|
||||||
|
python3 docs/graph/validate_ontology.py docs
|
||||||
|
python3 docs/graph/audit_drift.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 数据库备份与迁移
|
||||||
|
|
||||||
|
### 5.1 备份证据
|
||||||
|
|
||||||
|
使用权限受控的 MySQL option file,避免把密码写在命令行。备份必须包含表结构、数据、触发器、事件和存储过程,并记录文件大小、SHA-256、开始/结束时间。示例:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mysqldump --defaults-extra-file=/secure/path/petstore-mysql.cnf \
|
||||||
|
--single-transaction --routines --triggers --events --hex-blob \
|
||||||
|
petstore > /secure/backup/petstore-before-<rc>-<timestamp>.sql
|
||||||
|
shasum -a 256 /secure/backup/petstore-before-<rc>-<timestamp>.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
上线前必须把该备份恢复到隔离数据库并完成基础计数核对;“命令退出 0”不能替代恢复演练。备份保留周期由数据负责人决定,本流程不自动删除任何备份。
|
||||||
|
|
||||||
|
### 5.2 固定迁移顺序
|
||||||
|
|
||||||
|
在维护窗口中按以下顺序人工执行,每一步都保存执行时间和脚本 SHA-256,并检查脚本末尾验证查询:
|
||||||
|
|
||||||
|
1. `backend/db/migrations/20260801_split_service_identity.sql`
|
||||||
|
2. `backend/db/migrations/20260801_create_store_customer.sql`
|
||||||
|
3. `backend/db/migrations/20260801_create_business_event.sql`
|
||||||
|
4. `backend/db/migrations/20260801_create_booking_capacity.sql`
|
||||||
|
|
||||||
|
禁止跳步、交换顺序或让应用自动补表。任一验证项非 0,停止发布并由 Data Model/业务 owner 判断修复方案;不得猜测性回填客户身份、报告作者或事件。
|
||||||
|
|
||||||
|
### 5.3 生产只读预检
|
||||||
|
|
||||||
|
把 `backend/deploy/petstore-backend.env.example` 复制到服务器权限受控的 `/etc/petstore/backend.env` 并填入真实值;文件权限建议 `0600`,所有者为服务账号。不要在交互 shell 中 `source` 该文件,也不要用 `env $(cat ...)`,避免特殊字符被 shell 解释或泄露。通过一次性 systemd unit 以与正式服务相同的 `EnvironmentFile` 和服务账号执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemd-run --wait --pipe --collect \
|
||||||
|
--unit=petstore-preflight-<rc> \
|
||||||
|
--property=Type=oneshot \
|
||||||
|
--property=User=petstore \
|
||||||
|
--property=Group=petstore \
|
||||||
|
--property=WorkingDirectory=/opt/petstore/backend/releases/<rc> \
|
||||||
|
--property=EnvironmentFile=/etc/petstore/backend.env \
|
||||||
|
/usr/bin/env BUILD_FIRST=0 \
|
||||||
|
JAR_PATH=/opt/petstore/backend/releases/<rc>/petstore-backend.jar \
|
||||||
|
./deploy/release-preflight.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
通过条件:静态配置检查通过、JPA `validate` 通过、15 项数据不变量全部为 0,并以退出码 0 结束。预检不会执行迁移或修复数据。
|
||||||
|
|
||||||
|
## 6. 构建发布产物
|
||||||
|
|
||||||
|
### 6.1 后端
|
||||||
|
|
||||||
|
将已冻结 jar 放入 `/opt/petstore/backend/releases/<rc>/petstore-backend.jar`,同时归档 commit、SHA-256 和 `deploy/` 模板。systemd unit 以 `backend/deploy/petstore-backend.service.example` 为基线,由服务器管理员安装为 Petstore 独立 unit。
|
||||||
|
|
||||||
|
### 6.2 Admin 与 H5
|
||||||
|
|
||||||
|
真实域名配置保存在权限受控文件,不提交仓库。门禁和构建必须使用同一份内容:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/build/petstore/admin
|
||||||
|
PETSTORE_ADMIN_ENV_FILE=/secure/path/petstore-admin.env npm run preflight:release
|
||||||
|
cp /secure/path/petstore-admin.env .env.production.local
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
cd /opt/build/petstore/frontend
|
||||||
|
PETSTORE_FRONTEND_ENV_FILE=/secure/path/petstore-h5.env npm run preflight:release
|
||||||
|
cp /secure/path/petstore-h5.env .env.production.local
|
||||||
|
npm run build:h5
|
||||||
|
```
|
||||||
|
|
||||||
|
产物进入版本目录后再切换 `current` 软链接。构建结束应清除工作区内临时 `.env.*.local`,但保留受控配置源和构建证据。
|
||||||
|
|
||||||
|
### 6.3 微信小程序
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/build/petstore/frontend
|
||||||
|
PETSTORE_FRONTEND_ENV_FILE=/secure/path/petstore-mp.env npm run preflight:release
|
||||||
|
cp /secure/path/petstore-mp.env .env.mp-weixin.local
|
||||||
|
npm run build:mp-weixin
|
||||||
|
```
|
||||||
|
|
||||||
|
微信公众平台的 `request` / `downloadFile` 合法域名必须与门禁通过的实际域名一致。先上传体验版并完成真机三角色验收,再决定提审/发布;后端或 H5 发布不会自动更新用户手机里的小程序。
|
||||||
|
|
||||||
|
## 7. 切换应用与 Nginx
|
||||||
|
|
||||||
|
1. 将后端 `current` 指向新版本目录,执行 `systemctl daemon-reload`,只重启 Petstore 独立 unit。
|
||||||
|
2. 确认进程稳定并通过本机 readiness,再切 admin/H5 静态目录 `current`。
|
||||||
|
3. 以 `backend/deploy/nginx-petstore.conf.example` 新建 Petstore 专用配置文件;替换域名、证书路径和静态根目录。
|
||||||
|
4. 执行 `nginx -t`。检查配置 diff 仅增加/修改 Petstore 的三个 server block,不改 Gitea/GitLab upstream、端口、证书和 location。
|
||||||
|
5. 只执行 Nginx reload,不停止 Nginx;reload 后同时检查 Petstore、Gitea、GitLab 的既有健康入口。
|
||||||
|
|
||||||
|
证书续签必须沿用服务器现有 ACME/证书管理方式,为 Petstore 新域名单独申请/续签;不要替换其他产品证书。续签后先 `nginx -t` 再 reload,并记录证书到期时间。
|
||||||
|
|
||||||
|
## 8. 上线验证
|
||||||
|
|
||||||
|
### 8.1 自动只读 smoke
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/petstore/backend/current
|
||||||
|
API_ORIGIN=https://<api-domain> ./deploy/production-smoke.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
脚本验证 liveness、readiness、公开门店、服务时长和可约容量契约;提供受控的 `SMOKE_BEARER_TOKEN` 时才增加两个 protected GET。脚本不会创建或修改业务数据。
|
||||||
|
|
||||||
|
### 8.2 三角色人工 smoke
|
||||||
|
|
||||||
|
仅在预先标记的试点门店和测试宠主上执行,记录匿名化预约 ID/报告 ID,不记录完整 token/手机号:
|
||||||
|
|
||||||
|
1. **boss**:登录 admin,核对门店营业时段、并发容量、服务时长;创建员工;查看工作台、客户、日程。
|
||||||
|
2. **staff**:代客预约,开始服务,上传最小合规素材,提交一约一报告,复制报告话术;确认重复提交被拒绝。
|
||||||
|
3. **customer**:小程序自助预约并验证长服务跨时段占号;打开匿名报告页;提交提醒留资;再次预约。
|
||||||
|
4. **权限反例**:customer 不可进入后台;A 店账号不可读取 B 店预约/报告/线索;公开报告响应不含内部身份字段。
|
||||||
|
5. **异常反例**:无容量、非法状态迁移、缺前/后照片、错误 token、FFmpeg 失败均有可理解提示。
|
||||||
|
|
||||||
|
## 9. 回滚
|
||||||
|
|
||||||
|
### 9.1 应用与静态资源回滚
|
||||||
|
|
||||||
|
- 保留前一版本目录和软链接目标。若新版本 readiness、错误率或主链路失败,先停止切流,把 backend/admin/H5 的 `current` 恢复到 RC manifest 中的前一版本,再只重启 Petstore unit、`nginx -t` 并 reload。
|
||||||
|
- 微信小程序不能靠服务器软链接回滚。应在公众平台保留上一稳定版本,按微信流程回退;同时确认其 API 契约仍受当前后端兼容。
|
||||||
|
- 回滚后重新执行只读 smoke,并记录故障窗口、影响门店、回滚时间和 owner。
|
||||||
|
|
||||||
|
### 9.2 数据库回滚边界
|
||||||
|
|
||||||
|
本批四个迁移以新增列/表/索引和兼容回填为主,旧应用应忽略新增结构。因此常规回滚只回应用,不删除列、表、索引,不执行自动 down migration,也不把已产生的新业务数据覆盖回旧备份。
|
||||||
|
|
||||||
|
只有确认发生灾难性数据破坏、已冻结所有写入、明确接受备份时间点之后的数据丢失,并取得用户/业务负责人、DBA 和隐私责任人的书面授权后,才可执行整库恢复。恢复必须先在隔离库验证,再制定专门变更单;本 Runbook 不授权该操作。
|
||||||
|
|
||||||
|
## 10. 发布完成定义
|
||||||
|
|
||||||
|
只有以下证据全部归档,RC 才能由 `Ready for Release` 改为 `Released`:
|
||||||
|
|
||||||
|
- 四仓冻结 commit 与产物校验和;
|
||||||
|
- 备份及隔离恢复验证;
|
||||||
|
- 四个迁移执行和末尾验证结果;
|
||||||
|
- 最终配置门禁与后端只读预检;
|
||||||
|
- `nginx -t`、readiness、自动只读 smoke;
|
||||||
|
- boss/staff/customer 三角色人工 smoke;
|
||||||
|
- Gitea/GitLab 回归未受影响;
|
||||||
|
- 监控、告警联系人、回滚点和值班窗口;
|
||||||
|
- 未解决问题清单及 Go/No-Go 决策人。
|
||||||
91
生产监控与告警基线-2026-08-01.md
Normal file
91
生产监控与告警基线-2026-08-01.md
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
# 宠小它生产监控与告警基线
|
||||||
|
|
||||||
|
> 适用阶段:3~5 家单店试点。阈值是首版基线,上线两周后按真实流量校准。当前仓库只提供健康探针与运行手册;外部监控、日志平台和通知渠道必须在生产发布前实际接入并留证。
|
||||||
|
|
||||||
|
## 1. 监控目标
|
||||||
|
|
||||||
|
按优先级守住四件事:
|
||||||
|
|
||||||
|
1. 预约、开始服务、提交报告、打开报告的主链路可用;
|
||||||
|
2. 不产生跨店、身份错绑、状态回退、重复报告或容量超卖;
|
||||||
|
3. 数据库、上传目录、磁盘、FFmpeg 和 TLS 等运行依赖可用;
|
||||||
|
4. 监控本身不泄露密码、手机号、完整 token、微信标识或私密媒体 URL。
|
||||||
|
|
||||||
|
## 2. 健康探针
|
||||||
|
|
||||||
|
| 探针 | 地址 | 含义 | 使用方式 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Liveness | `/actuator/health/liveness` | JVM/Spring 是否存活 | 连续失败才重启,避免业务依赖短抖导致重启风暴 |
|
||||||
|
| Readiness | `/actuator/health/readiness` | DB、磁盘、上传目录、FFmpeg/ffprobe 是否可用 | 非 UP 立即摘流,不把请求送给未就绪实例 |
|
||||||
|
|
||||||
|
生产 `show-details=never`,公网响应只显示总状态。探针日志不得附环境变量值。`petstoreRuntime` 结果缓存 30 秒,避免每次 readiness 都启动外部进程。
|
||||||
|
|
||||||
|
## 3. 首版告警阈值
|
||||||
|
|
||||||
|
| 信号 | Warning | Critical | Owner |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Readiness | 1 次失败 | 连续 2 次失败或持续 2 分钟 | Backend Ops |
|
||||||
|
| HTTP 5xx | 5 分钟 ≥ 2% 或 ≥ 5 次 | 5 分钟 ≥ 5% 或 ≥ 20 次 | Backend Ops + 业务 owner |
|
||||||
|
| API p95 | 10 分钟 > 1 秒 | 10 分钟 > 2 秒 | Backend Ops |
|
||||||
|
| DB 连接池 | active/max 持续 5 分钟 > 70% | 持续 5 分钟 > 85% 或获取超时 | Backend Ops / Data Model |
|
||||||
|
| 磁盘 | 可用 < 20% 或 < 20 GB | 可用 < 10% 或 < 10 GB | Backend Ops |
|
||||||
|
| 上传失败 | 10 分钟 ≥ 3 次 | 10 分钟 ≥ 10 次或连续失败 | Report Media Backend |
|
||||||
|
| 成片失败 | 30 分钟失败率 ≥ 10% | ≥ 20% 或连续 3 个任务失败 | Report Media Backend |
|
||||||
|
| TLS | 距到期 < 21 天 | 距到期 < 7 天 | Backend Ops |
|
||||||
|
| 主链路人工救援 | 当日 ≥ 1 次 | 同类问题当日 ≥ 3 次 | Core Flow QA / PM |
|
||||||
|
| 权限或隐私异常 | 不设 Warning | 任意 1 次即 Critical | System Architect / Privacy owner |
|
||||||
|
|
||||||
|
低流量试点同时看比例和绝对数量,避免一个请求造成噪音;权限、隐私和数据串店不做流量豁免。
|
||||||
|
|
||||||
|
## 4. 应用与业务信号
|
||||||
|
|
||||||
|
### 技术指标
|
||||||
|
|
||||||
|
- 按 endpoint family 聚合请求量、2xx/4xx/5xx、p50/p95/p99,不记录 query 中的 token;
|
||||||
|
- JVM heap、GC pause、线程数、进程重启次数;
|
||||||
|
- Hikari active/idle/pending/max 和获取连接耗时;
|
||||||
|
- 上传目录容量、inode、读写失败;
|
||||||
|
- FFmpeg/ffprobe 可执行性、成片耗时、失败归类(material/service/network/unknown);
|
||||||
|
- Nginx 499/502/503/504、上游延迟、证书到期;
|
||||||
|
- 每次发布的 commit、时间、操作者和 readiness 变化标记。
|
||||||
|
|
||||||
|
### 业务漏斗
|
||||||
|
|
||||||
|
每天按门店聚合 `appointment_created → service_started → service_completed/report_submitted → report_opened → lead_submitted → rebooked`。试点初期不把业务漏斗波动直接当系统故障,但需检查:
|
||||||
|
|
||||||
|
- 有 done 无报告、报告无客户/作者、重复报告;
|
||||||
|
- BusinessEvent 缺失或幂等键重复;
|
||||||
|
- 服务时长/预约快照非法、容量超卖;
|
||||||
|
- 报告已提交但长期未打开;
|
||||||
|
- 回访后没有明确状态或再次预约归因。
|
||||||
|
|
||||||
|
发布前的 `release-preflight.sh` 已覆盖前四类结构/数据不变量;运行期应每天做等价的只读审计或报表,不自动修复。
|
||||||
|
|
||||||
|
## 5. 日志与隐私
|
||||||
|
|
||||||
|
- 采用结构化字段:timestamp、level、traceId、endpoint、status、duration、storeId(内部受控);
|
||||||
|
- 不记录请求/响应全文,不记录密码、验证码、AppSecret、session token、完整 report token、完整手机号、openid/unionid 或媒体签名 URL;
|
||||||
|
- 公开报告只允许使用不可逆短 hash 做技术关联,业务事件表本身不保存 token/hash;
|
||||||
|
- 错误对用户返回稳定业务码和可理解文案,堆栈只进入受控日志;
|
||||||
|
- 生产日志访问采用最小权限,导出/截图前脱敏,并设置保留周期。
|
||||||
|
|
||||||
|
## 6. 告警处理流程
|
||||||
|
|
||||||
|
1. 告警必须带:环境、时间、信号名、当前值、受影响 endpoint/门店数量、最近发布版本;不得带敏感值。
|
||||||
|
2. 值班人先判断是否为探针、依赖或业务错误;readiness Critical 先摘流,权限/隐私 Critical 先关闭相关入口。
|
||||||
|
3. 15 分钟内明确 owner 和下一动作;30 分钟仍未恢复则启动回滚评估。
|
||||||
|
4. 若问题与新版本相关且旧应用兼容当前数据库,按 Runbook 切回上一版本;禁止为了快速恢复而删新列/表或直接改业务状态。
|
||||||
|
5. 恢复后补时间线、根因、影响、修复、验证和预防项;事故报告不粘贴真实 token/手机号/媒体 URL。
|
||||||
|
|
||||||
|
## 7. 上线前接入清单
|
||||||
|
|
||||||
|
- [ ] 外部 HTTPS 探测覆盖 liveness/readiness、admin、H5 报告入口;
|
||||||
|
- [ ] 5xx、延迟、DB 池、磁盘、证书和进程重启已配置告警;
|
||||||
|
- [ ] FFmpeg/上传失败有可聚合字段,而非只能搜索自由文本;
|
||||||
|
- [ ] BusinessEvent 日漏斗和 15 项只读数据审计有每日结果;
|
||||||
|
- [ ] 通知渠道、Primary/Backup 值班人和升级联系人完成真实测试;
|
||||||
|
- [ ] 日志脱敏抽查通过,访问权限和保留周期已批准;
|
||||||
|
- [ ] 发布标记能关联四仓 commit 与 RC;
|
||||||
|
- [ ] Gitea/GitLab 有独立监控,Petstore 发布后做旁路回归。
|
||||||
|
|
||||||
|
未完成以上接入,RC 只能保持 `Ready for Release`。
|
||||||
119
试点门店开通清单-2026-08-01.md
Normal file
119
试点门店开通清单-2026-08-01.md
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
# 宠小它试点门店开通清单
|
||||||
|
|
||||||
|
> 目标:让 3~5 家独立宠物洗护单店用真实业务完成约 200 次“预约 → 服务 → 报告 → 查看/回访 → 再次预约”闭环,以数据决定 Phase 2 是否进入商业化。本文是开通和验收清单,不代表任何门店已经开通。
|
||||||
|
|
||||||
|
## 1. 试点准入
|
||||||
|
|
||||||
|
每家门店必须满足:
|
||||||
|
|
||||||
|
- 有一名可决策的老板和一名日常负责人,接受至少 4 周连续试用;
|
||||||
|
- 主要业务是宠物洗护/美容,愿意把真实预约和服务报告录入系统;
|
||||||
|
- 明确同意试点数据用途、照片/视频告知方式、数据保留与退出处理;
|
||||||
|
- 可配置真实营业时段、同时接待数、服务项目和预计时长;
|
||||||
|
- 有可用手机/电脑、稳定网络和微信小程序使用条件;
|
||||||
|
- 接受上线首周每日反馈、之后每周复盘,不要求支付、库存、储值、复杂会员、多门店组织等本期外能力。
|
||||||
|
|
||||||
|
不满足隐私告知、负责人、真实业务量或反馈承诺的门店,不进入试点。
|
||||||
|
|
||||||
|
## 2. 门店建档卡
|
||||||
|
|
||||||
|
每店单独保存,敏感信息进入权限受控系统,不写入公开文档或提交记录:
|
||||||
|
|
||||||
|
| 项目 | 填写要求 |
|
||||||
|
|---|---|
|
||||||
|
| 试点门店编号 | 使用内部编号,不用公开昵称代替主键 |
|
||||||
|
| 老板 / 日常负责人 | 姓名、联系方式、可响应时间(受控保存) |
|
||||||
|
| 预计周服务量 | 用于判断 200 次样本达成时间 |
|
||||||
|
| 营业日与营业时段 | 精确到半小时容量桶 |
|
||||||
|
| 同时接待数 | 1~10,必须与现场工位/人力真实匹配 |
|
||||||
|
| 服务项目 | 名称、预计时长(30~480 分钟,30 分钟倍数) |
|
||||||
|
| 员工名单 | 最小必要账号;离职/退出当天停用 |
|
||||||
|
| 门店 logo/电话/地址 | 宠主公开页允许展示的内容,经门店确认 |
|
||||||
|
| 素材告知方式 | 何时向宠主说明拍摄、报告链接和转发风险 |
|
||||||
|
| 支持群与升级联系人 | 产品、技术、隐私各自 owner |
|
||||||
|
| 上线日期 / 退出日期 | 明确试点窗口和退出处理 |
|
||||||
|
|
||||||
|
## 3. 开通前配置
|
||||||
|
|
||||||
|
- [ ] 创建门店和 boss,首次登录后更换/确认安全登录方式。
|
||||||
|
- [ ] boss 创建最小必要 staff;customer 不能进入 admin。
|
||||||
|
- [ ] 核对门店公开字段,不展示邀请码、ownerId 等内部信息。
|
||||||
|
- [ ] 配置营业开始、最后容量桶和真实 `bookingCapacity`。
|
||||||
|
- [ ] 配置全部在售服务项目及 `durationMinutes`;不使用笼统默认时长掩盖差异。
|
||||||
|
- [ ] 建立午休/停约 `blocked` 与到店客 `walk_in` 的操作约定。
|
||||||
|
- [ ] 用未来一天验证:长服务覆盖全部时段、超容量不可约、取消后释放容量。
|
||||||
|
- [ ] 清除 demo 门店/万能验证码/测试宠主和无效服务项目;测试记录与真实数据可区分。
|
||||||
|
- [ ] 确认报告前后照片最少各一张、服务过程素材可选、报告提交后锁定。
|
||||||
|
- [ ] 告知员工:完整报告 token、手机号和媒体链接不得贴入群聊、工单或截图。
|
||||||
|
|
||||||
|
## 4. 上线日三角色验收
|
||||||
|
|
||||||
|
### Boss
|
||||||
|
|
||||||
|
- 登录 admin,查看工作台、预约、客户、日程、报告、线索和设置;
|
||||||
|
- 新增/停用一个试点员工;确认跨店数据不可见;
|
||||||
|
- 调整一个服务时长或容量配置,并由第二人复核;
|
||||||
|
- 确认当天异常处理和人工回退联系人。
|
||||||
|
|
||||||
|
### Staff
|
||||||
|
|
||||||
|
- 按手机号关联真实宠主做一次代客预约,员工身份不被写成宠主;
|
||||||
|
- 开始服务、上传前/过程/后素材、提交报告并发送给宠主;
|
||||||
|
- 验证重复报告、缺照片、非法状态迁移均被拒绝;
|
||||||
|
- 在日程中登记一次 `walk_in`,必要时登记一次 `blocked`。
|
||||||
|
|
||||||
|
### Customer
|
||||||
|
|
||||||
|
- 扫门店入口,选择服务和可约时段,提交预约;
|
||||||
|
- 打开报告公开页,确认门店与宠物信息准确且没有内部字段;
|
||||||
|
- 提交提醒留资,重复提交不生成重复线索;
|
||||||
|
- 从报告页再次预约,验证门店和服务上下文连续。
|
||||||
|
|
||||||
|
## 5. 试点期支持节奏
|
||||||
|
|
||||||
|
- 上线前 2 天:完成培训、模拟全链路、回滚演练和联系人确认。
|
||||||
|
- 上线第 1~3 天:每日核对预约、完成、报告、报告打开、留资和失败记录。
|
||||||
|
- 第 2 周起:每周一次 30 分钟复盘,先解决阻塞履约的问题,不以新增页面代替流程修复。
|
||||||
|
- 严重权限/隐私/数据错误:立即停用相关入口并升级;容量或状态脏数据:暂停新增预约直至核对完成。
|
||||||
|
- 门店退出:停用账号和入口,按约定导出/保留/删除数据,并记录完成证据。
|
||||||
|
|
||||||
|
## 6. 200 次完整服务的统计口径
|
||||||
|
|
||||||
|
“一次完整服务”必须同时满足:
|
||||||
|
|
||||||
|
1. 有真实 `Appointment`,客户身份和创建人语义正确;
|
||||||
|
2. 合法 `new → doing → done`;
|
||||||
|
3. 有且仅有一份绑定报告,至少包含服务前/后照片;
|
||||||
|
4. 报告已产生可发送链接;
|
||||||
|
5. BusinessEvent 可追溯预约创建、服务开始、服务完成/报告提交。
|
||||||
|
|
||||||
|
样本按门店、服务类型、创建来源(customer/staff/boss)分层,测试预约、取消预约、重复数据和纯演示数据不计入 200 次。
|
||||||
|
|
||||||
|
## 7. 每周指标
|
||||||
|
|
||||||
|
| 指标 | 口径 | 用途 |
|
||||||
|
|---|---|---|
|
||||||
|
| 完整服务数 | 符合上方五项的服务 | 判断样本进度 |
|
||||||
|
| 预约完成率 | done / 有效预约 | 判断主链路稳定性 |
|
||||||
|
| 报告提交率 | 有报告的 done / done | 判断员工执行成本 |
|
||||||
|
| 报告打开率 | 首次打开报告 / 已提交报告 | 判断宠主价值 |
|
||||||
|
| 留资率 | 去重留资 / 首次打开报告 | 判断回访意愿 |
|
||||||
|
| 再次预约率 | 报告后窗口内产生新预约的 StoreCustomer / 可观察客户 | 判断闭环价值 |
|
||||||
|
| 容量冲突率 | 因无容量被拒绝的请求 / 可约请求 | 校准容量而非承诺零等待 |
|
||||||
|
| 人工救援率 | 需技术/运营改数据或绕流程的服务 / 完整服务尝试 | 判断是否可规模化 |
|
||||||
|
| P0 故障数 | 权限、数据、主链路阻断 | Go/No-Go 硬门槛 |
|
||||||
|
|
||||||
|
指标只使用最小必要业务 ID 和聚合数;周报不展示完整手机号、token 或私密媒体 URL。
|
||||||
|
|
||||||
|
## 8. 阶段退出标准
|
||||||
|
|
||||||
|
进入 Phase 2 决策评审前,至少满足:
|
||||||
|
|
||||||
|
- 3~5 家门店真实运行,累计约 200 次完整服务;
|
||||||
|
- 连续两周无未解决的 P0 权限、隐私或数据一致性事故;
|
||||||
|
- 核心链路不依赖开发者直接改库或手工补状态;
|
||||||
|
- 老板与员工能独立完成日常预约、服务、报告和回访;
|
||||||
|
- 再次预约闭环有可统计证据,而不是仅有页面或口头反馈;
|
||||||
|
- 各门店的继续使用意愿、付费假设和主要阻碍被记录。
|
||||||
|
|
||||||
|
未满足时继续修复产品内核,不提前引入支付、库存、储值、复杂会员、多门店组织或泛 AI。
|
||||||
Loading…
Reference in New Issue
Block a user