修复退款复核与发布安全边界
补齐退款人工复核、刷新恢复、会话隔离和旧冷备导入兼容 强化真实微信退款对账开关、重复环境变量和生产发布门禁 优化公开作品资产授权查询并保持实时撤销语义 统一后台微信渠道、退款追回流水和画布正式资源测试契约 补齐生成与素材侧栏回归测试
This commit is contained in:
@@ -56,6 +56,8 @@ function main() {
|
||||
assertDeployRejectsRelativeReleaseRoot();
|
||||
assertDeployRejectsRelativeCurrentLink();
|
||||
assertDeployRejectsRelativeApiEnvFile();
|
||||
assertRealWechatPayRequiresRefundReconciliation();
|
||||
assertRealWechatPayUsesLastRefundReconciliationAssignment();
|
||||
assertDeployCleansStagingReleaseOnFailure();
|
||||
assertDeployRejectsFinalReleaseRaceAndCleansStaging();
|
||||
assertMissingBackupScriptFails();
|
||||
@@ -128,6 +130,11 @@ function assertDeployCopiesPingoraDirectReleaseDependencies() {
|
||||
'[production-api-deploy] 完成:',
|
||||
'部署成功时必须输出完成信息。',
|
||||
);
|
||||
assertIncludes(
|
||||
readFileSync(fixture.apiEnvFile, 'utf8'),
|
||||
'WECHAT_PAY_REFUND_RECONCILIATION_ENABLED=true',
|
||||
'生产部署必须为存量 API env 补齐退款 reconciliation 开关。',
|
||||
);
|
||||
|
||||
const releaseDir = path.join(fixture.releaseRoot, fixture.version);
|
||||
const currentTarget = readlinkSync(fixture.currentLink);
|
||||
@@ -354,6 +361,48 @@ function assertDeployCopiesPingoraDirectReleaseDependencies() {
|
||||
}
|
||||
}
|
||||
|
||||
function assertRealWechatPayRequiresRefundReconciliation() {
|
||||
const fixture = prepareFixture('wechat-refund-reconciliation-disabled');
|
||||
writeFileSync(
|
||||
fixture.apiEnvFile,
|
||||
`${readFileSync(fixture.apiEnvFile, 'utf8')}WECHAT_PAY_ENABLED=true\nWECHAT_PAY_PROVIDER=real\nWECHAT_PAY_REFUND_RECONCILIATION_ENABLED=false\n`,
|
||||
'utf8',
|
||||
);
|
||||
const result = runDeploy(fixture);
|
||||
|
||||
if (result.status === 0) {
|
||||
failures.push('真实微信支付关闭退款 reconciliation 时部署必须失败。');
|
||||
}
|
||||
assertIncludes(
|
||||
result.stderr,
|
||||
'真实微信支付必须设置 WECHAT_PAY_REFUND_RECONCILIATION_ENABLED=true',
|
||||
'退款 reconciliation 门禁失败时必须给出明确错误。',
|
||||
);
|
||||
assertMaintenanceCleared(fixture, '退款 reconciliation 门禁失败');
|
||||
}
|
||||
|
||||
function assertRealWechatPayUsesLastRefundReconciliationAssignment() {
|
||||
const fixture = prepareFixture('wechat-refund-reconciliation-duplicate-disabled-last');
|
||||
writeFileSync(
|
||||
fixture.apiEnvFile,
|
||||
`${readFileSync(fixture.apiEnvFile, 'utf8')}WECHAT_PAY_ENABLED=true\nWECHAT_PAY_PROVIDER=real\nWECHAT_PAY_REFUND_RECONCILIATION_ENABLED=true\nWECHAT_PAY_REFUND_RECONCILIATION_ENABLED=false\n`,
|
||||
'utf8',
|
||||
);
|
||||
const result = runDeploy(fixture);
|
||||
|
||||
if (result.status === 0) {
|
||||
failures.push(
|
||||
'退款 reconciliation 重复配置时必须按 systemd EnvironmentFile 的最后一次赋值拦截部署。',
|
||||
);
|
||||
}
|
||||
assertIncludes(
|
||||
result.stderr,
|
||||
'真实微信支付必须设置 WECHAT_PAY_REFUND_RECONCILIATION_ENABLED=true',
|
||||
'退款 reconciliation 重复配置的最后一项为 false 时必须给出明确错误。',
|
||||
);
|
||||
assertMaintenanceCleared(fixture, '退款 reconciliation 重复配置门禁失败');
|
||||
}
|
||||
|
||||
function assertDeployRestartsActivePingoraWhenArtifactIncluded() {
|
||||
const fixture = prepareFixture('with-active-pingora-artifact');
|
||||
addPingoraGatewayArtifact(fixture);
|
||||
|
||||
@@ -196,6 +196,16 @@ const checks = [
|
||||
'GENARRATIVE_SPACETIME_RUNTIME_SERVICE_BOOTSTRAP_SECRET_FILE=/var/lib/genarrative/spacetime/runtime-service-bootstrap-secret.txt',
|
||||
reason: '生产 API env 示例只能登记 bootstrap secret 的固定 FILE 路径。',
|
||||
},
|
||||
{
|
||||
file: 'deploy/env/api-server.env.example',
|
||||
includes: 'WECHAT_PAY_REFUND_RECONCILIATION_ENABLED=true',
|
||||
reason: '真实微信支付 release 必须在生产 env 示例中显式开启退款主动对账。',
|
||||
},
|
||||
{
|
||||
file: 'scripts/deploy/production-api-deploy.sh',
|
||||
includes: 'validate_real_wechat_pay_refund_reconciliation',
|
||||
reason: 'API deploy 必须在 current 切换前复核真实微信支付退款 reconciliation。',
|
||||
},
|
||||
{
|
||||
file: 'jenkins/Jenkinsfile.production-stdb-module-publish',
|
||||
includes: "file(credentialsId: params.MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID.trim(), variable: 'MIGRATION_BOOTSTRAP_SECRET_FILE')",
|
||||
|
||||
@@ -149,6 +149,7 @@ path = Path(sys.argv[1])
|
||||
key = sys.argv[2]
|
||||
if not path.exists():
|
||||
raise SystemExit(0)
|
||||
matched_value = None
|
||||
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
||||
line = raw_line.strip()
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
@@ -158,8 +159,9 @@ for raw_line in path.read_text(encoding="utf-8").splitlines():
|
||||
value = value.strip()
|
||||
if len(value) >= 2 and value[0] == value[-1] and value[0] in ("\"", "'\''"):
|
||||
value = value[1:-1]
|
||||
print(value)
|
||||
raise SystemExit(0)
|
||||
matched_value = value
|
||||
if matched_value is not None:
|
||||
print(matched_value)
|
||||
'
|
||||
|
||||
if [[ -r "${file_path}" ]]; then
|
||||
@@ -297,6 +299,7 @@ ensure_runtime_env_and_dirs() {
|
||||
ensure_env_value "${api_env_file}" "GENARRATIVE_WALLET_REFUND_OUTBOX_BATCH_SIZE" "100"
|
||||
ensure_env_value "${api_env_file}" "GENARRATIVE_WALLET_REFUND_OUTBOX_FLUSH_INTERVAL_MS" "1000"
|
||||
ensure_env_value "${api_env_file}" "GENARRATIVE_WALLET_REFUND_OUTBOX_MAX_BYTES" "67108864"
|
||||
ensure_env_value "${api_env_file}" "WECHAT_PAY_REFUND_RECONCILIATION_ENABLED" "true"
|
||||
ensure_env_value_migrates_old_default "${api_env_file}" "GENARRATIVE_EXTERNAL_GENERATION_WORKER_LEASE_SECONDS" "3600" "600"
|
||||
ensure_env_value "${api_env_file}" "GENARRATIVE_EXTERNAL_GENERATION_WORKER_JOB_TIMEOUT_SECONDS" "900"
|
||||
ensure_env_value "${api_env_file}" "GENARRATIVE_EXTERNAL_GENERATION_WORKER_LONG_JOB_TIMEOUT_SECONDS" "1800"
|
||||
@@ -317,6 +320,20 @@ ensure_runtime_env_and_dirs() {
|
||||
fi
|
||||
}
|
||||
|
||||
validate_real_wechat_pay_refund_reconciliation() {
|
||||
local api_env_file="$1"
|
||||
local pay_enabled pay_provider reconciliation_enabled
|
||||
|
||||
pay_enabled="$(read_env_value "${api_env_file}" "WECHAT_PAY_ENABLED" | tr '[:upper:]' '[:lower:]')"
|
||||
pay_provider="$(read_env_value "${api_env_file}" "WECHAT_PAY_PROVIDER" | tr '[:upper:]' '[:lower:]')"
|
||||
reconciliation_enabled="$(read_env_value "${api_env_file}" "WECHAT_PAY_REFUND_RECONCILIATION_ENABLED" | tr '[:upper:]' '[:lower:]')"
|
||||
if [[ "${pay_enabled}" == "true" && "${pay_provider}" == "real" && "${reconciliation_enabled}" != "true" ]]; then
|
||||
echo "[production-api-deploy] 真实微信支付必须设置 WECHAT_PAY_REFUND_RECONCILIATION_ENABLED=true: ${api_env_file}" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "[production-api-deploy] 微信退款 reconciliation 配置已复核: enabled=${reconciliation_enabled:-unset}, real_pay=$([[ "${pay_enabled}" == "true" && "${pay_provider}" == "real" ]] && printf true || printf false)"
|
||||
}
|
||||
|
||||
ensure_worker_runtime_env_defaults() {
|
||||
local worker_env_file="$1"
|
||||
|
||||
@@ -1110,6 +1127,7 @@ if [[ -n "${SPACETIME_SERVER_URL}" ]]; then
|
||||
fi
|
||||
|
||||
ensure_runtime_env_and_dirs "${API_ENV_FILE}"
|
||||
validate_real_wechat_pay_refund_reconciliation "${API_ENV_FILE}"
|
||||
ensure_worker_runtime_env_defaults "${WORKER_ENV_FILE}"
|
||||
migrate_legacy_editor_generation_pricing_override "${CURRENT_LINK}"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user