Files
sub2api/backend/migrations/134_affiliate_ledger_audit_snapshots.sql

86 lines
3.9 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- 邀请返利流水补充订单关联和转余额快照。
-- 这些字段只用于审计展示;历史旧流水无法可靠反推的字段保持 NULL避免把当前状态误展示为历史状态。
ALTER TABLE user_affiliate_ledger
ADD COLUMN IF NOT EXISTS source_order_id BIGINT NULL REFERENCES payment_orders(id) ON DELETE SET NULL;
ALTER TABLE user_affiliate_ledger
ADD COLUMN IF NOT EXISTS balance_after DECIMAL(20,8) NULL;
ALTER TABLE user_affiliate_ledger
ADD COLUMN IF NOT EXISTS aff_quota_after DECIMAL(20,8) NULL;
ALTER TABLE user_affiliate_ledger
ADD COLUMN IF NOT EXISTS aff_frozen_quota_after DECIMAL(20,8) NULL;
ALTER TABLE user_affiliate_ledger
ADD COLUMN IF NOT EXISTS aff_history_quota_after DECIMAL(20,8) NULL;
COMMENT ON COLUMN user_affiliate_ledger.source_order_id IS '产生该返利流水的充值订单;转余额或无法可靠回填的历史数据为 NULL';
COMMENT ON COLUMN user_affiliate_ledger.balance_after IS '邀请返利转余额后的用户余额快照;无法取得时为 NULL';
COMMENT ON COLUMN user_affiliate_ledger.aff_quota_after IS '邀请返利转余额后的可用返利额度快照;无法取得时为 NULL';
COMMENT ON COLUMN user_affiliate_ledger.aff_frozen_quota_after IS '邀请返利转余额后的冻结返利额度快照;无法取得时为 NULL';
COMMENT ON COLUMN user_affiliate_ledger.aff_history_quota_after IS '邀请返利转余额后的历史返利总额快照;无法取得时为 NULL';
CREATE INDEX IF NOT EXISTS idx_user_affiliate_ledger_source_order_id
ON user_affiliate_ledger(source_order_id)
WHERE source_order_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_user_affiliate_ledger_rebate_lookup
ON user_affiliate_ledger(action, source_order_id, user_id, source_user_id, created_at)
WHERE action = 'accrue';
-- 尽力回填 PR #2169 合并后、该迁移前已经产生的返利流水。
-- 只有在同一订单只能匹配到一条返利流水时才回填,避免把多笔同额流水错误绑定到订单。
WITH rebate_audits AS (
SELECT po.id AS order_id,
po.user_id AS invitee_user_id,
invitee_aff.inviter_id,
rebate_detail.rebate_amount,
pal.created_at AS audit_created_at
FROM payment_audit_logs pal
CROSS JOIN LATERAL (
SELECT substring(
pal.detail
FROM '"rebateAmount"[[:space:]]*:[[:space:]]*(-?[0-9]+(\.[0-9]+)?)'
)::numeric AS rebate_amount
) rebate_detail
JOIN payment_orders po ON po.id::text = pal.order_id
JOIN user_affiliates invitee_aff ON invitee_aff.user_id = po.user_id
WHERE pal.action = 'AFFILIATE_REBATE_APPLIED'
AND rebate_detail.rebate_amount IS NOT NULL
),
ranked_matches AS (
SELECT ual.id AS ledger_id,
ra.order_id,
COUNT(*) OVER (PARTITION BY ra.order_id) AS order_match_count,
COUNT(*) OVER (PARTITION BY ual.id) AS ledger_match_count,
ROW_NUMBER() OVER (
PARTITION BY ual.id
ORDER BY ABS(EXTRACT(EPOCH FROM (ual.created_at - ra.audit_created_at))), ra.order_id
) AS ledger_rank
FROM rebate_audits ra
JOIN user_affiliate_ledger ual
ON ual.action = 'accrue'
AND ual.source_order_id IS NULL
AND ual.user_id = ra.inviter_id
AND ual.source_user_id = ra.invitee_user_id
AND ABS(ual.amount - ra.rebate_amount) < 0.00000001
AND ual.created_at BETWEEN ra.audit_created_at - INTERVAL '10 minutes'
AND ra.audit_created_at + INTERVAL '10 minutes'
)
UPDATE user_affiliate_ledger ual
SET source_order_id = ranked_matches.order_id,
updated_at = NOW()
FROM ranked_matches
WHERE ual.id = ranked_matches.ledger_id
AND ranked_matches.order_match_count = 1
AND ranked_matches.ledger_match_count = 1
AND ranked_matches.ledger_rank = 1
AND NOT EXISTS (
SELECT 1
FROM user_affiliate_ledger existing
WHERE existing.source_order_id = ranked_matches.order_id
AND existing.action = 'accrue'
);