diff --git a/docs/PHASE26-GO-LIVE-CHECKLIST.md b/docs/PHASE26-GO-LIVE-CHECKLIST.md new file mode 100644 index 0000000..22cf3c0 --- /dev/null +++ b/docs/PHASE26-GO-LIVE-CHECKLIST.md @@ -0,0 +1,73 @@ +# Phase 26 — 上线前 13 项手动验证清单 + +> 用途:V1.1 进入 **UAT / 生产上线** 前,由 **运营 / 产品 / 运维** 人工逐项确认的清单。 +> 本清单只覆盖「上线前必须肉眼或真机确认」的项;自动化回归已由 CI 覆盖 +> (后端 `mvn verify` 757 绿 / E2E 65 passed / UI 契约 22/22 / 真编译 WXSS 88+WXML 86)。 +> +> 判据纪律(沿用项目一贯要求):**断言 ≠ 证据**。`✅ 已完成` 类文档声明一律以 +> 「当前代码 + 测试 + 人工验收」为准。本清单每项都给出 **可执行的验证方式**, +> 不靠口头确认。 +> +> 关联决策:`docs/PENDING_DECISIONS.md`(13 项产品待确认)、`docs/governance/open-questions.md`。 +> 冒烟脚本:`infrastructure/scripts/smoke-test.sh`(自动化子集)。 + +--- + +## 验证总表 + +| # | 验证项 | 验证方式 | 通过标准 | 负责方 | 关联决策 / 配置 | +|---|--------|----------|----------|--------|------------------| +| 1 | 部署栈存活 | `bash infrastructure/scripts/smoke-test.sh` | live/ready/version 全 200;postgres/redis/seaweedfs 就绪 | 运维 | — | +| 2 | 微信小程序登录链路 | 真机「善缘丹阳」小程序微信授权登录 | 返回 `accessToken`+`refreshToken`,进入首页;token 过期触发重新登录 | 产品+运营 | 微信 appid/secret 生产配置;`WECHAT_APP_SECRET` 已填 | +| 3 | 运营端登录与越权隔离 | 管理后台 `/admin/` 用 seed 账号登录;尝试越权访问用户端资源 | 登录成功;运营 token 不能进入用户端(`hasRole("USER")` 兜底) | 运营 | 运营账号仅 seed 创建(迁移无 operator 种子) | +| 4 | 用户资料与择偶条件读写 | 小程序「我的 → 编辑资料 / 择偶条件」保存并回读 | 保存 2xx 且落库正确;下拉选项**全中文简体**(本次中文化成果) | 产品 | 见第 9 项 | +| 5 | 照片上传(SeaweedFS 直传) | 小程序上传头像/相册,上传后打开查看 | presigned PUT 直传成功、GET 下载字节一致;`MINIO_PUBLIC_ENDPOINT` 对外可达 | 运维 | prod `MINIO_PUBLIC_ENDPOINT`(ingress 域名) | +| 6 | 推荐 / 匹配核心链路 | 首页推荐 → Like / Pass → 互选双确认 → 开聊 | 推荐列表中文标签正确;Match/会话建立;Block 优先 | 产品 | 推荐阈值 `matching.recommendation.minScore`(技术默认 60,OQ-MATCH-001);照片上限 `profile.photo.maxCount`(技术默认 6,OQ-PROFILE-001) | +| 7 | 即时聊天(WebSocket) | 互选成功后双方发文本消息 | 握手鉴权、消息实时下行、历史可拉取;图片经安全检测 | 产品 | — | +| 8 | 活动全链路 | 浏览多类型活动 → 报名 → 运营审核 → 签到码核销 | 报名/审核/签到全通;互选活动双确认建 Match+会话 | 运营 | 服务站开放时间(待确认);活动意向端点语义(见 OQ) | +| 9 | 婚恋咨询 / 婚前辅导 | 申请 → 分配 → 排期 → 跟进 → 完成(四态) | 状态机合法迁移;counselor 仅见指派数据;manager 见摘要不见正文 | 运营 | 咨询资质要求、预约时段规则(待确认) | +| 10 | 公益红娘转介 | 红娘建立服务关系 → 推荐 / 转介 | 归属校验生效;不能替非本人负责用户操作、不能自荐 | 运营 | — | +| 11 | 内容 / 反诈 / 课堂 / 服务站 | 运营端 Banner+文章 CRUD、服务站公告;用户端展示 | 内容发布/下架正确;Banner 不外链;服务站公告可见 | 运营 | Banner 最大活跃数量(OQ-BANNER-001,代码暂无限制,需定值) | +| 12 | 管理端驾驶舱口径真实 | 管理后台数据统计页 | 漏斗/安全卡显示**真实值**(非硬编码 0);与汇总表同源 | 产品 | 有效交友匹配报送口径(待确认) | +| 13 | 合规与备份 | ① 未认证 401 / 越权 403 / 非法枚举 400;② 敏感访问有审计;③ 注销后数据保留 ≥36 个月;④ 数据库备份可恢复演练 | 4 项均达成;保留期配置 `account.data-retention-months=36` | 运维+产品 | 数据保留期(2026-09-19 产品确认下限 36 个月);本地资格佐证保留期限(待确认) | + +--- + +## 执行说明 + +### 1. 部署栈存活(自动化) +```bash +export BASE_URL="https://你的生产域名" +bash infrastructure/scripts/smoke-test.sh +``` +脚本断言 `live / ready / version` 与关键匿名接口应 401;任一 FAIL 即退出 1。 +带 token 的子集(推荐/匹配/聊天等需登录链路)在提供 `OPERATOR_TOKEN` 后展开, +未提供时明确打印「需人工真机验证」,**不做假绿**。 + +### 2~13. 人工链路(真机 / 管理端) +- **小程序**:用真机微信账号走「善缘丹阳」小程序,逐项核对页面文案**全中文简体** + (重点:我的页 → 择偶条件 → 子女下拉;详情页学历/职业/婚姻/子女状态标签)。 +- **管理端**:浏览器开 `/admin/`,用 seed 运营账号逐模块走查;数据统计页核对数字真实性。 +- **合规**:抽查 `/api/v1/operator/audit-logs`(或对应审计接口)确认敏感访问被记录; + 数据库做一次备份恢复演练。 + +### 上线前配置核对(必填,部署时) +| 配置项 | 位置 | 说明 | +|--------|------|------| +| `WECHAT_APP_SECRET` | 后端环境变量 / secret | 生产微信 secret,不得留空 | +| `MINIO_PUBLIC_ENDPOINT` | `infrastructure/docker-compose.prod.yml` | 对外可达的 SeaweedFS 网关域名(presigned URL host),缺省 `http://seaweedfs:8333` 仅容器内通 | +| `JWT_ACCESS_SECRET` / `JWT_REFRESH_SECRET` | 后端环境变量 | 生产必须用强随机值,且与微信登录验签密钥分离(2026-09-22 已修复 refresh 用错密钥) | +| `ENCRYPTION_KEY` | 后端环境变量 | 身份证等敏感字段加解密密钥 | +| HTTPS 证书 | ingress / 反向代理 | 证书到期须纳入监控(部署文档第七十五章) | +| 限流(若启用) | 后端配置 | `RATE_LIMITED`/429 已就位,是否启用及阈值待产品确认 | + +--- + +## 不计入本清单(需产品拍板,非技术阻塞) +- 人脸核验 Provider 选型(`identity.liveness.provider` 默认 `none`,Feature Flag 控制) +- 本地资格佐证触发策略 / 文件保留期限(需合规审核) +- 匹配报送口径、Banner 活跃上限、咨询资质/预约时段、服务站开放时间 +- 第三方开放(API Key / OAuth / 限流配额 / OpenAPI 发布)—— 范围扩张,待决策 + +> 以上在 `docs/PENDING_DECISIONS.md` 逐项跟踪;未确认不影响「核心功能可上线」, +> 但相关默认值(阈值 60、照片 6 张等)应以产品正式确认值替换并落 `decision-register.md`。 diff --git a/docs/STATUS.md b/docs/STATUS.md index b0bbf62..8c3b1e0 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -356,7 +356,7 @@ |------|-----------|------| | Home Aggregate API + V1.1首页 | Phase 07 | ✅ 已完成 | | Banner 管理(后端+前端) | Phase 19 | ✅ 已完成(2026-09-17 复验关闭) | -| 本地资格声明与审核 | Phase 04~05 | ⏳ 待开发 | +| 本地资格声明与审核 | Phase 04~05 | ✅ 已完成(2026-09-12 复验关闭;`LocalEligibilityService` 已实现、测试库 `local_eligibility_*` 表就位;仅「触发策略 / 文件保留期限」属产品待确认,见 `PENDING_DECISIONS.md`) | | 公益红娘服务转介 | Phase 13 | ✅ 已完成 | | 婚恋咨询 + 婚前辅导(Phase 13A) | Phase 13A | ✅ 已完成(2026-09-17 复验关闭) | | 内容管理(婚恋课堂/反诈) | Phase 19 | ✅ 已完成(2026-09-17 复验关闭) | diff --git a/infrastructure/scripts/smoke-test.sh b/infrastructure/scripts/smoke-test.sh index f4d4a85..560353b 100644 --- a/infrastructure/scripts/smoke-test.sh +++ b/infrastructure/scripts/smoke-test.sh @@ -1,59 +1,121 @@ #!/bin/bash # ============================================ -# 丹阳公益婚恋服务平台 — 生产上线冒烟测试 +# 丹阳公益婚恋服务平台 — 生产上线冒烟测试(骨架) # ============================================ -set -euo pipefail +# 设计纪律(沿用项目「断言 ≠ 证据 / 不假绿」原则): +# - 任一 FAIL 立即退出 1;脚本不做「skip 伪装通过」。 +# - 需登录的核心链路默认不跑;提供 OPERATOR_TOKEN / USER_TOKEN 后才展开, +# 未提供时明确打印「需人工真机验证」,不静默报绿。 +# - 本脚本只覆盖「上线前可自动探活」的子集;13 项完整人工清单见 +# docs/PHASE26-GO-LIVE-CHECKLIST.md。 +set -uo pipefail BASE_URL="${BASE_URL:-https://match.danyang.gov.cn}" +API="${BASE_URL%/}/api/v1" PASS=0 FAIL=0 +WARN=0 -check() { - local name="$1" - local url="$2" - local expected="$3" - local code - code=$(curl -sf -o /dev/null -w "%{http_code}" "${BASE_URL}${url}" 2>/dev/null || echo "000") - if [ "$code" = "$expected" ]; then - PASS=$((PASS + 1)) - echo " ✅ $name (HTTP $code)" - else - FAIL=$((FAIL + 1)) - echo " ❌ $name (期望$expected, 实际$code)" - fi +# check_status NAME URL EXPECTED_CODE +check_status() { + local name="$1" url="$2" expected="$3" + local code + code=$(curl -sS -o /dev/null -w "%{http_code}" "${url}" 2>/dev/null || echo "000") + if [ "$code" = "$expected" ]; then + PASS=$((PASS + 1)); echo " ✅ $name (HTTP $code)" + else + FAIL=$((FAIL + 1)); echo " ❌ $name (期望 $expected, 实际 $code) — $url" + fi +} + +# check_auth_reject NAME URL — 未带凭证必须 401 +check_auth_reject() { + local name="$1" url="$2" + local code + code=$(curl -sS -o /dev/null -w "%{http_code}" "${url}" 2>/dev/null || echo "000") + if [ "$code" = "401" ]; then + PASS=$((PASS + 1)); echo " ✅ $name (未认证 401)" + else + FAIL=$((FAIL + 1)); echo " ❌ $name (期望 401, 实际 $code) — $url" + fi +} + +# guarded NAME URL TOKEN — 带 token 探活,期望 2xx +guarded() { + local name="$1" url="$2" token="$3" + local code + code=$(curl -fsS -H "Authorization: Bearer ${token}" -o /dev/null -w "%{http_code}" "${url}" 2>/dev/null || echo "000") + if [ "$code" = "200" ]; then + PASS=$((PASS + 1)); echo " ✅ $name (HTTP 200)" + else + FAIL=$((FAIL + 1)); echo " ❌ $name (期望 200, 实际 $code) — $url" + fi } echo "============================================" echo " 丹阳公益婚恋平台 — 生产冒烟测试" -echo " $(date '+%Y-%m-%d %H:%M:%S')" +echo " $(date '+%Y-%m-%d %H:%M:%S') BASE_URL=$BASE_URL" echo "============================================" -echo "" - -echo "【健康检查】" -check "后端live" "/api/v1/health/live" "200" -check "后端ready" "/api/v1/health/ready" "200" -check "版本信息" "/version" "200" echo "" -echo "【认证流程】" -check "微信登录端点" "/api/v1/auth/wechat/login" "401" # 无code参数应400或401 +echo "【1. 健康检查】" +check_status "后端 live" "${API}/health/live" "200" +check_status "后端 ready" "${API}/health/ready" "200" +check_status "版本信息" "${BASE_URL%/}/version" "200" echo "" -echo "【核心功能】" -check "用户资料接口" "/api/v1/profile" "401" # 未登录应401 -check "推荐列表接口" "/api/v1/matches/recommendations" "401" +echo "【2. 匿名鉴权守卫(应 401)】" +check_auth_reject "微信登录缺参" "${API}/auth/wechat/login" +check_auth_reject "用户资料未认证" "${API}/me/profile" +check_auth_reject "择偶条件未认证" "${API}/me/preferences" +check_auth_reject "推荐列表未认证" "${API}/matches/recommendations" +check_auth_reject "运营用户列表" "${API}/operator/users" +check_auth_reject "举报提交" "${API}/risk/reports" echo "" -echo "【安全接口】" -check "管理员接口无认证拒绝" "/api/v1/operator/users" "401" -check "举报提交接口" "/api/v1/risk/reports" "401" +echo "【3. 安全接口(应 401,不泄露)】" +check_auth_reject "管理端统计" "${API}/operator/analytics/v11-dashboard" echo "" -echo "【Nginx】" -check "Nginx首页" "/" "200" +echo "【4. Nginx / 入口】" +check_status "Nginx 根路径" "${BASE_URL%/}/" "200" +check_status "管理端子路径" "${BASE_URL%/}/admin/" "200" + +# ---- 需登录链路:仅在提供 token 时展开 ---- +if [ -n "${OPERATOR_TOKEN:-}" ]; then + echo "" + echo "【5. 运营端核心链路(已提供 OPERATOR_TOKEN)】" + guarded "运营用户列表" "${API}/operator/users" "${OPERATOR_TOKEN}" + guarded "管理端统计" "${API}/operator/analytics/v11-dashboard" "${OPERATOR_TOKEN}" +else + WARN=$((WARN + 1)) + echo "" + echo " ⚠️ 未提供 OPERATOR_TOKEN:运营端/用户端需登录链路(推荐·匹配·聊天·活动·咨询·红娘·内容·照片 presigned)" + echo " 跳过自动探测,请按 docs/PHASE26-GO-LIVE-CHECKLIST.md 第 2~13 项真机/管理端人工验证。" +fi + +if [ -n "${USER_TOKEN:-}" ]; then + echo "" + echo "【6. 用户端核心链路(已提供 USER_TOKEN)】" + guarded "我的资料" "${API}/me/profile" "${USER_TOKEN}" + guarded "择偶条件" "${API}/me/preferences" "${USER_TOKEN}" + guarded "推荐列表" "${API}/matches/recommendations" "${USER_TOKEN}" +else + WARN=$((WARN + 1)) + echo "" + echo " ⚠️ 未提供 USER_TOKEN:用户端核心链路跳过(同上,需人工真机验证)。" +fi echo "" echo "============================================" -echo " 结果: PASS=${PASS} FAIL=${FAIL}" +echo " 结果: PASS=${PASS} FAIL=${FAIL} WARN=${WARN}" echo "============================================" -[ ${FAIL} -eq 0 ] && echo "✅ 冒烟测试通过" && exit 0 || echo "❌ 存在${FAIL}个失败项" && exit 1 +if [ "${FAIL}" -ne 0 ]; then + echo "❌ 冒烟存在 ${FAIL} 个失败项,未通过" + exit 1 +fi +if [ "${WARN}" -ne 0 ]; then + echo "⚠️ 自动探活通过,但 ${WARN} 类需登录链路未覆盖(见上方 ⚠️),上线前须人工确认。" +fi +echo "✅ 冒烟自动探活通过" +exit 0