-- 1) Normalize historical affiliate rebate rate values. -- Legacy compatibility treated 0 20%). -- We now use pure percentage semantics, so convert persisted fractional values once. UPDATE settings SET value = to_char((value::numeric * 100), 'FM999999990.########'), updated_at = NOW() WHERE key = 'affiliate_rebate_rate' AND value ~ '^-?[0-9]+(\\.[0-9]+)?$' AND value::numeric > 0 AND value::numeric <= 1; -- 2) Affiliate ledger for accrual/transfer traceability. CREATE TABLE IF NOT EXISTS user_affiliate_ledger ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, action VARCHAR(32) NOT NULL, amount DECIMAL(20,8) NOT NULL, source_user_id BIGINT NULL REFERENCES users(id) ON DELETE SET NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_user_affiliate_ledger_user_id ON user_affiliate_ledger(user_id); CREATE INDEX IF NOT EXISTS idx_user_affiliate_ledger_action ON user_affiliate_ledger(action); COMMENT ON TABLE user_affiliate_ledger IS '邀请返利资金流水(累计/转入)'; COMMENT ON COLUMN user_affiliate_ledger.action IS 'accrue|transfer'; -- 3) Enforce idempotency at DB layer for payment audit actions. WITH ranked AS ( SELECT id, ROW_NUMBER() OVER (PARTITION BY order_id, action ORDER BY id) AS rn FROM payment_audit_logs ) DELETE FROM payment_audit_logs p USING ranked r WHERE p.id = r.id AND r.rn > 1; CREATE UNIQUE INDEX IF NOT EXISTS idx_payment_audit_logs_order_action_uniq ON payment_audit_logs(order_id, action); -- 4) Prevent retroactive affiliate rebate issuance for legacy completed balance orders. INSERT INTO payment_audit_logs (order_id, action, detail, operator, created_at) SELECT po.id::text, 'AFFILIATE_REBATE_SKIPPED', '{"reason":"baseline before affiliate rebate idempotency rollout"}', 'system', NOW() FROM payment_orders po WHERE po.order_type = 'balance' AND po.status = 'COMPLETED' AND NOT EXISTS ( SELECT 1 FROM payment_audit_logs pal WHERE pal.order_id = po.id::text AND pal.action IN ('AFFILIATE_REBATE_APPLIED', 'AFFILIATE_REBATE_SKIPPED') );