#!/usr/bin/env bash # Phase A 门店后台 API 冒烟(需 backend :8080 + MySQL) # 用法: # export MYSQL_PASSWORD=... # 先启动 backend # export BOSS_PHONE=13800000001 # export SMS_CODE=123456 # bash docs/qa-reports/scripts/admin-phase-a-api-smoke.sh set -euo pipefail BASE="${API_BASE:-http://127.0.0.1:8080/api}" PHONE="${BOSS_PHONE:-}" CODE="${SMS_CODE:-123456}" red() { printf '\033[31m%s\033[0m\n' "$*"; } green() { printf '\033[32m%s\033[0m\n' "$*"; } step() { printf '\n== %s ==\n' "$*"; } need() { if [[ -z "${1:-}" ]]; then red "缺少 $2" exit 1 fi } need "$PHONE" "BOSS_PHONE(老板/员工手机号)" json_get() { python3 -c 'import json,sys; d=json.load(sys.stdin); print(d'"$1"')' } step "0) 健康:公开门店列表" curl -sf "$BASE/store/list" | python3 -c 'import json,sys; d=json.load(sys.stdin); assert d.get("code")==200; print("ok stores", len(d.get("data") or []))' step "1) 登录" LOGIN=$(curl -sf -X POST "$BASE/user/login" -H 'Content-Type: application/json' \ -d "{\"phone\":\"$PHONE\",\"code\":\"$CODE\"}") echo "$LOGIN" | python3 -c 'import json,sys; d=json.load(sys.stdin); assert d.get("code")==200, d; print("role", d["data"]["user"]["role"])' TOKEN=$(echo "$LOGIN" | json_get '["data"]["sessionToken"]') ROLE=$(echo "$LOGIN" | json_get '["data"]["user"]["role"]') AUTH="Authorization: Bearer $TOKEN" if [[ "$ROLE" != "boss" && "$ROLE" != "staff" ]]; then red "需要 boss/staff,当前 role=$ROLE" exit 1 fi auth_get() { curl -sf -H "$AUTH" "$@"; } auth_post() { curl -sf -H "$AUTH" -H 'Content-Type: application/json' -X POST "$@"; } step "2) customer 门禁:用 customer 应 403(若有)— 跳过;校验 admin store" auth_get "$BASE/admin/store" | python3 -c 'import json,sys; d=json.load(sys.stdin); assert d.get("code")==200, d; assert "inviteCode" not in d["data"]; assert "onboardingStatus" in d["data"]; print("store", d["data"].get("name"), "onboarding", d["data"].get("onboardingStatus"))' step "3) 预约列表(含 admin enrichment)" auth_get "$BASE/appointment/list?page=1&pageSize=20" | python3 -c ' import json,sys d=json.load(sys.stdin) assert d.get("code")==200, d rows=d.get("data") or [] print("appointments", len(rows)) if rows: r=rows[0] for k in ("hasReport","customerPhoneMasked","staffName"): assert k in r, f"missing {k} in {r.keys()}" print("enrichment ok") ' step "4) 服务客户聚合" auth_get "$BASE/admin/service-customers?page=1&pageSize=20" | python3 -c ' import json,sys d=json.load(sys.stdin) assert d.get("code")==200, d data=d["data"] assert "summary" in data and "list" in data s=data["summary"] for k in ("customerCount","pendingLeadCount","monthVisitCount"): assert k in s assert "totalBalance" not in s print("customers", data.get("total"), "summary", s) ' step "5) 线索 / 报告 / 排班" TODAY=$(date +%F) auth_get "$BASE/report/leads?status=pending" | python3 -c 'import json,sys; d=json.load(sys.stdin); assert d.get("code")==200, d; rows=d.get("data") or []; assert all("wechatOpenid" not in r for r in rows); print("leads", len(rows))' auth_get "$BASE/report/list?page=1&pageSize=20" | python3 -c 'import json,sys; d=json.load(sys.stdin); assert d.get("code")==200, d; rows=d.get("data") or []; assert not rows or "updateTime" in rows[0]; print("reports", len(rows))' auth_get "$BASE/schedule/day?date=$TODAY" | python3 -c 'import json,sys; d=json.load(sys.stdin); assert d.get("code")==200, d; print("schedule rows", len((d.get("data") or {}).get("rows") or []))' step "6) 员工列表" auth_get "$BASE/user/staff-list" | python3 -c 'import json,sys; d=json.load(sys.stdin); assert d.get("code")==200, d; print("staff", len(d.get("data") or []))' green "API smoke PASS"