恢复付款入口与常开基线(08-01-reimbursement-notification-progress-clarity)
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

这个提交包含在:
2026-08-01 23:12:23 +08:00
父节点 aa6b9bc373
当前提交 37b4ef2d10
共修改 20 个文件,包含 655 行新增和 244 行删除
+15 -102
查看文件
@@ -6,6 +6,7 @@ CLIENT_DIR="$ROOT_DIR/client"
PUBLIC_BASE_URL="${LIVE_SMOKE_BASE_URL:-https://oa.hyinda.com}"
REPORT_DIR="${FIN_PROD_ENABLE_REPORT_DIR:-$ROOT_DIR/artifacts/qa-flow/fin-prod-enable-$(date +%Y%m%d-%H%M%S)}"
SUMMARY_TXT="$REPORT_DIR/summary.txt"
FLAG_SNAPSHOT_FILE="$REPORT_DIR/payment-batch-flag-before.json"
STATUS=0
mkdir -p "$REPORT_DIR"
@@ -124,101 +125,18 @@ if (expected === "force_off" && (!paymentOff || !erpOff)) {
NODE
}
force_payment_batch_flag_off() {
node <<'NODE'
const baseUrl = (process.env.LIVE_SMOKE_BASE_URL || "https://oa.hyinda.com").replace(/\/$/, "");
const identifier = process.env.LIVE_SMOKE_ADMIN_IDENTIFIER?.trim();
const password = process.env.LIVE_SMOKE_ADMIN_PASSWORD?.trim();
const issueId = process.env.QA_ISSUE_ID?.trim() || "FINPROD";
if (!identifier || !password) {
console.error("[fin-prod-enable][rollback] missing admin credentials");
process.exit(1);
}
async function fetchJson(url, init = {}) {
const response = await fetch(url, {
...init,
signal: AbortSignal.timeout(20_000),
});
const text = await response.text();
let json = null;
try {
json = text ? JSON.parse(text) : null;
} catch {
json = null;
}
return { response, json };
}
function errorSummary(json, status) {
const error = json?.error ?? {};
return `${status} ${error.code ?? ""} ${error.message ?? ""}`.trim();
}
const login = await fetchJson(`${baseUrl}/api/auth/login`, {
method: "POST",
headers: {
"content-type": "application/json",
"x-device-fingerprint": "fin-prod-enable-rollback",
},
body: JSON.stringify({ identifier, password }),
});
if (!login.response.ok || !login.json?.data?.accessToken) {
console.error(`[fin-prod-enable][rollback] login failed: ${errorSummary(login.json, login.response.status)}`);
process.exit(1);
}
const token = login.json.data.accessToken;
const list = await fetchJson(`${baseUrl}/api/system/feature-flags`, {
headers: { authorization: `Bearer ${token}`, accept: "application/json" },
});
if (!list.response.ok) {
console.error(`[fin-prod-enable][rollback] list flags failed: ${errorSummary(list.json, list.response.status)}`);
process.exit(1);
}
const flag = list.json?.data?.items?.find((item) => item.flagKey === "payment_batch_v1");
if (!flag) {
console.error("[fin-prod-enable][rollback] missing payment_batch_v1");
process.exit(1);
}
if (flag.overrideMode === "force_off" && flag.effectiveEnabled === false) {
console.log("[fin-prod-enable][rollback] payment_batch_v1 already force_off");
process.exit(0);
}
const updated = await fetchJson(`${baseUrl}/api/system/feature-flags/payment_batch_v1`, {
method: "PUT",
headers: {
authorization: `Bearer ${token}`,
"content-type": "application/json",
accept: "application/json",
},
body: JSON.stringify({
overrideMode: "force_off",
rolloutType: "global",
departmentIds: [],
note: `${issueId} failed final rollback`,
expectedVersion: flag.version,
}),
});
if (!updated.response.ok) {
console.error(`[fin-prod-enable][rollback] update failed: ${errorSummary(updated.json, updated.response.status)}`);
process.exit(1);
}
const item = updated.json?.data?.item;
console.log(`[fin-prod-enable][rollback] payment_batch_v1 override=${item?.overrideMode ?? "unknown"} effective=${item?.effectiveEnabled ?? "unknown"}`);
NODE
restore_payment_batch_flag_snapshot() {
P2_FIN_LIVE_APPROVED=1 LIVE_SMOKE_BASE_URL="$PUBLIC_BASE_URL" \
node "$ROOT_DIR/scripts/qa/payment-batch-flag-snapshot.mjs" restore \
--snapshot-file "$FLAG_SNAPSHOT_FILE"
}
write_summary_json() {
local qa_status="PASS"
local notes="FINPROD 正式启用:公网付款批次验收通过后保持 payment_batch_v1=force_on;失败时回滚 force_off;不接真实支付、不接 ERP、不开放图片 OCR。"
local notes="FINPROD 正式启用:公网付款批次验收通过后保持 payment_batch_v1=force_on;失败时恢复调用前完整快照;不接真实支付、不接 ERP、不开放图片 OCR。"
if [[ "$STATUS" -ne 0 ]]; then
qa_status="FAIL"
notes="FINPROD 正式启用失败或门禁失败:已尝试将 payment_batch_v1 回滚到 force_off;不接真实支付、不接 ERP、不开放图片 OCR。"
notes="FINPROD 正式启用失败或门禁失败:已尝试恢复 payment_batch_v1 调用前完整快照;不接真实支付、不接 ERP、不开放图片 OCR。"
fi
QA_REPORT_DIR="$REPORT_DIR" \
@@ -243,21 +161,13 @@ finish() {
fi
fi
if [[ "$STATUS" -ne 0 ]]; then
echo ">>> [fin-prod-enable] failure-force-flag-off" | tee -a "$SUMMARY_TXT"
if force_payment_batch_flag_off > >(tee "$REPORT_DIR/failure-force-flag-off.log") 2>&1; then
echo "[fin-prod-enable][PASS] failure-force-flag-off" | tee -a "$SUMMARY_TXT"
if [[ "$STATUS" -ne 0 && -f "$FLAG_SNAPSHOT_FILE" ]]; then
echo ">>> [fin-prod-enable] restore-caller-flag-snapshot" | tee -a "$SUMMARY_TXT"
if restore_payment_batch_flag_snapshot > >(tee "$REPORT_DIR/restore-caller-flag-snapshot.log") 2>&1; then
echo "[fin-prod-enable][PASS] restore-caller-flag-snapshot" | tee -a "$SUMMARY_TXT"
else
local code="$?"
echo "[fin-prod-enable][FAIL] failure-force-flag-off exit=$code" | tee -a "$SUMMARY_TXT"
fi
echo ">>> [fin-prod-enable] final-flag-off-check" | tee -a "$SUMMARY_TXT"
if LIVE_SMOKE_BASE_URL="$PUBLIC_BASE_URL" fetch_feature_flags_node "force_off" > >(tee "$REPORT_DIR/final-flag-off-check.log") 2>&1; then
echo "[fin-prod-enable][PASS] final-flag-off-check" | tee -a "$SUMMARY_TXT"
else
local code="$?"
echo "[fin-prod-enable][FAIL] final-flag-off-check exit=$code" | tee -a "$SUMMARY_TXT"
echo "[fin-prod-enable][FAIL] restore-caller-flag-snapshot exit=$code" | tee -a "$SUMMARY_TXT"
fi
fi
fi
@@ -283,6 +193,9 @@ run_shell_step "live-env-strict" "LIVE_SMOKE_BASE_URL='$PUBLIC_BASE_URL' node '$
run_shell_step "pre-flag-off-check" "LIVE_SMOKE_BASE_URL='$PUBLIC_BASE_URL' node '$ROOT_DIR/scripts/qa/check-payment-batch-flag-off.mjs' --json"
[[ "$STATUS" -eq 0 ]] || exit "$STATUS"
run_shell_step "capture-caller-flag-snapshot" "P2_FIN_LIVE_APPROVED=1 LIVE_SMOKE_BASE_URL='$PUBLIC_BASE_URL' node '$ROOT_DIR/scripts/qa/payment-batch-flag-snapshot.mjs' capture --snapshot-file '$FLAG_SNAPSHOT_FILE'"
[[ "$STATUS" -eq 0 ]] || exit "$STATUS"
run_shell_step "fin-prod-enable-spec" "cd '$CLIENT_DIR' && PLAYWRIGHT_MANUAL=1 E2E_MODE=live P2_FIN_LIVE_APPROVED=1 P2_FIN_LIVE_FINAL_STATE=force_on QA_ISSUE_ID='${QA_ISSUE_ID:-FINPROD}' P2_FIN_LIVE_MARKER_PREFIX='${P2_FIN_LIVE_MARKER_PREFIX:-${QA_ISSUE_ID:-FINPROD}}' P2_FIN_LIVE_REPORT_DIR='$REPORT_DIR' LIVE_SMOKE_BASE_URL='$PUBLIC_BASE_URL' npx playwright test tests/e2e/manual/p2-fin-live-payment-batch.spec.ts --workers=1 --reporter=list"
if [[ "${FIN_PROD_ENABLE_SKIP_MOBILE:-0}" == "1" ]]; then