285 lines
8.9 KiB
SQL
285 lines
8.9 KiB
SQL
-- =============================================================================
|
||
-- 修复订单 Q_176967208700018143d9f6:支付宝已退款成功,但库内未完成
|
||
-- =============================================================================
|
||
--
|
||
-- 【原因简述】
|
||
-- 后台发起支付宝退款后,支付宝侧已退款成功,但创建退款记录时因 unique_refund_no
|
||
-- 冲突(Duplicate entry 'refund-Q_176967208700018143d9f6')导致 createRefundRecordAndUpdateOrder
|
||
-- 失败,后续流程未执行:订单未置为 refunded、未写退款记录、未扣代理佣金与钱包。
|
||
--
|
||
-- 【本单实际退款金额】支付宝已退 99.5 元(非整单金额)
|
||
--
|
||
-- 【与 adminrefundorderlogic 退款链路对照】
|
||
-- 代码路径:AdminRefundOrder -> handleAlipayRefund -> createRefundRecordAndUpdateOrder + HandleCommissionAndWalletDeduction
|
||
--
|
||
-- handleAlipayRefund 成功分支:
|
||
-- 1) createRefundRecordAndUpdateOrder(order, req, refundNo, refundResp.TradeNo, ...)
|
||
-- -> 事务内:Insert order_refund(refund_no, platform_refund_id=TradeNo, order_id, user_id, product_id, refund_amount, status=success, refund_time=NOW())
|
||
-- -> 事务内:Update order(仅 code 中赋了 status=refunded,未显式设 refund_time/version)
|
||
-- 2) HandleCommissionAndWalletDeduction(ctx, svcCtx, nil, order, req.RefundAmount)
|
||
-- -> 该订单下 agent_commission:按 refundAmount 比例冲减,refunded_amount 增加,满额则 status=2(UpdateWithVersion)
|
||
-- -> 按代理汇总扣减额,agent_wallet 先扣冻结再扣可用(UpdateWithVersion)
|
||
-- -> agent_wallet_transaction 插入 type=refund、金额为负的流水
|
||
--
|
||
-- 本 SQL 对应关系:
|
||
-- Step 2 = createRefundRecordAndUpdateOrder 的 Insert order_refund(platform_refund_id 修复时无支付宝 TradeNo 填 NULL,可从支付宝补)
|
||
-- Step 3 = createRefundRecordAndUpdateOrder 的 Update order,并显式补全 refund_time、version+1
|
||
-- Step 4 = HandleCommissionAndWalletDeduction 对 agent_commission 的冲减(按 99.5 比例)
|
||
-- Step 5 = HandleCommissionAndWalletDeduction 对 agent_wallet 扣减 + agent_wallet_transaction 插入
|
||
--
|
||
-- 【执行前请确认】
|
||
-- 1)该订单在支付宝侧已退款成功;2)已备份相关表或先在测试环境执行。
|
||
-- Step 2~5 已包在事务中:任一步报错请执行 ROLLBACK;若 step 1 未查到订单(_order_not_found=1)勿执行后续。
|
||
-- =============================================================================
|
||
|
||
-- 与表字段 collation 一致,避免 #1267 Illegal mix of collations(若表为 utf8mb4_general_ci 则改为 COLLATE utf8mb4_general_ci)
|
||
SET
|
||
@order_no = CONVERT(
|
||
'Q_176967208700018143d9f6' USING utf8mb4
|
||
) COLLATE utf8mb4_general_ci;
|
||
-- 本单实际退款金额(元)
|
||
SET @repair_refund_amount = 99.5;
|
||
|
||
-- 1) 取订单信息
|
||
SELECT
|
||
id,
|
||
user_id,
|
||
product_id,
|
||
amount,
|
||
version INTO @order_id,
|
||
@user_id,
|
||
@product_id,
|
||
@order_amount,
|
||
@order_version
|
||
FROM `order`
|
||
WHERE
|
||
order_no = @order_no
|
||
AND del_state = 0
|
||
LIMIT 1;
|
||
|
||
SET @refund_amount = @repair_refund_amount;
|
||
|
||
-- 若无记录则说明订单号错误,终止
|
||
SELECT IF(@order_id IS NULL, 1, 0) AS _order_not_found;
|
||
-- 若 _order_not_found=1 请勿继续执行后续语句
|
||
|
||
-- ---------- 以下为事务:任一步报错请执行 ROLLBACK ----------
|
||
START TRANSACTION;
|
||
|
||
-- 2) 补写退款记录(若该订单尚无成功状态的退款记录)
|
||
-- 代码中 platform_refund_id 来自支付宝 refundResp.TradeNo;修复时无则填 NULL,如有支付宝退款单号可事后 UPDATE 补上
|
||
INSERT INTO
|
||
order_refund (
|
||
refund_no,
|
||
order_id,
|
||
user_id,
|
||
product_id,
|
||
platform_refund_id,
|
||
refund_amount,
|
||
refund_reason,
|
||
status,
|
||
del_state,
|
||
version,
|
||
refund_time,
|
||
close_time,
|
||
delete_time
|
||
)
|
||
SELECT
|
||
CONCAT(
|
||
'refund-',
|
||
@order_no,
|
||
'-repair'
|
||
),
|
||
@order_id,
|
||
@user_id,
|
||
@product_id,
|
||
NULL,
|
||
@refund_amount,
|
||
NULL,
|
||
'success',
|
||
0,
|
||
0,
|
||
NOW(),
|
||
NULL,
|
||
NULL
|
||
FROM (
|
||
SELECT 1
|
||
) _one
|
||
WHERE
|
||
NOT EXISTS (
|
||
SELECT 1
|
||
FROM order_refund
|
||
WHERE
|
||
order_id = @order_id
|
||
AND status = 'success'
|
||
AND del_state = 0
|
||
);
|
||
|
||
-- 3) 订单状态改为已退款
|
||
UPDATE `order`
|
||
SET
|
||
status = 'refunded',
|
||
refund_time = NOW(),
|
||
version = version + 1,
|
||
update_time = NOW()
|
||
WHERE
|
||
id = @order_id
|
||
AND del_state = 0
|
||
AND status = 'paid';
|
||
|
||
-- 4) 代理佣金:按本次退款 99.5 元在该订单的佣金上比例冲减(refunded_amount 增加,满额则 status=2)
|
||
SET
|
||
@total_available = (
|
||
SELECT COALESCE(
|
||
SUM(amount - refunded_amount), 0
|
||
)
|
||
FROM agent_commission
|
||
WHERE
|
||
order_id = @order_id
|
||
AND del_state = 0
|
||
);
|
||
|
||
UPDATE agent_commission
|
||
SET
|
||
refunded_amount = LEAST(
|
||
amount,
|
||
refunded_amount + @repair_refund_amount * (amount - refunded_amount) / NULLIF(@total_available, 0)
|
||
),
|
||
status = CASE
|
||
WHEN LEAST(
|
||
amount,
|
||
refunded_amount + @repair_refund_amount * (amount - refunded_amount) / NULLIF(@total_available, 0)
|
||
) >= amount THEN 2
|
||
ELSE status
|
||
END,
|
||
version = version + 1,
|
||
update_time = NOW()
|
||
WHERE
|
||
order_id = @order_id
|
||
AND del_state = 0
|
||
AND @total_available > 0;
|
||
|
||
-- 5) 代理钱包扣减 + 流水(仅对尚未存在本单退款流水的代理扣减,避免重复执行导致重复扣款)
|
||
DROP TEMPORARY TABLE IF EXISTS _repair_wallet_snapshot;
|
||
|
||
CREATE TEMPORARY TABLE _repair_wallet_snapshot (
|
||
agent_id BIGINT NOT NULL,
|
||
balance_before DECIMAL(20, 4) NOT NULL,
|
||
frozen_before DECIMAL(20, 4) NOT NULL,
|
||
total_deduct DECIMAL(20, 4) NOT NULL,
|
||
PRIMARY KEY (agent_id)
|
||
);
|
||
|
||
-- 按 99.5 元在该订单各代理间按“可退佣金”比例分配扣减额(与 step 4 一致),仅扣减额>0 的代理
|
||
INSERT INTO
|
||
_repair_wallet_snapshot (
|
||
agent_id,
|
||
balance_before,
|
||
frozen_before,
|
||
total_deduct
|
||
)
|
||
SELECT
|
||
agent_id,
|
||
balance_before,
|
||
frozen_before,
|
||
total_deduct
|
||
FROM (
|
||
SELECT
|
||
c.agent_id, w.balance AS balance_before, w.frozen_balance AS frozen_before, @repair_refund_amount * COALESCE(
|
||
SUM(c.amount - c.refunded_amount), 0
|
||
) / NULLIF(@total_available, 0) AS total_deduct
|
||
FROM
|
||
agent_commission c
|
||
JOIN agent_wallet w ON w.agent_id = c.agent_id
|
||
AND w.del_state = 0
|
||
WHERE
|
||
c.order_id = @order_id
|
||
AND c.del_state = 0
|
||
AND @total_available > 0
|
||
AND NOT EXISTS (
|
||
SELECT 1
|
||
FROM agent_wallet_transaction t
|
||
WHERE
|
||
t.agent_id = c.agent_id
|
||
AND t.transaction_id = @order_no
|
||
AND t.transaction_type = 'refund'
|
||
AND t.del_state = 0
|
||
)
|
||
GROUP BY
|
||
c.agent_id, w.balance, w.frozen_balance
|
||
) _agent_deduct
|
||
WHERE
|
||
total_deduct > 0;
|
||
|
||
-- 从钱包扣减:优先扣冻结余额,不足再扣可用余额
|
||
UPDATE agent_wallet w
|
||
INNER JOIN _repair_wallet_snapshot r ON w.agent_id = r.agent_id
|
||
SET
|
||
w.frozen_balance = w.frozen_balance - LEAST(
|
||
r.total_deduct,
|
||
w.frozen_balance
|
||
),
|
||
w.balance = w.balance - (
|
||
r.total_deduct - LEAST(
|
||
r.total_deduct,
|
||
w.frozen_balance
|
||
)
|
||
),
|
||
w.version = w.version + 1,
|
||
w.update_time = NOW()
|
||
WHERE
|
||
w.del_state = 0;
|
||
|
||
-- 插入退款流水(金额为负数)
|
||
INSERT INTO
|
||
agent_wallet_transaction (
|
||
delete_time,
|
||
del_state,
|
||
version,
|
||
agent_id,
|
||
transaction_type,
|
||
amount,
|
||
balance_before,
|
||
balance_after,
|
||
frozen_balance_before,
|
||
frozen_balance_after,
|
||
transaction_id,
|
||
related_user_id,
|
||
remark
|
||
)
|
||
SELECT
|
||
NULL,
|
||
0,
|
||
0,
|
||
r.agent_id,
|
||
'refund',
|
||
- r.total_deduct,
|
||
r.balance_before,
|
||
r.balance_before - (
|
||
r.total_deduct - LEAST(
|
||
r.total_deduct,
|
||
r.frozen_before
|
||
)
|
||
),
|
||
r.frozen_before,
|
||
r.frozen_before - LEAST(
|
||
r.total_deduct,
|
||
r.frozen_before
|
||
),
|
||
@order_no,
|
||
NULL,
|
||
'订单退款修复(支付宝已退,库内补单)'
|
||
FROM _repair_wallet_snapshot r;
|
||
|
||
DROP TEMPORARY TABLE IF EXISTS _repair_wallet_snapshot;
|
||
|
||
COMMIT;
|
||
-- ---------- 事务结束 ----------
|
||
|
||
-- 6) 校验(可选)
|
||
-- 订单应为 refunded
|
||
-- SELECT id, order_no, status, refund_time FROM `order` WHERE id = @order_id;
|
||
-- 应有 success 退款记录,refund_amount = 99.5
|
||
-- SELECT * FROM order_refund WHERE order_id = @order_id AND del_state = 0;
|
||
-- 佣金 refunded_amount 按 99.5 比例增加,满额则 status=2
|
||
-- SELECT id, agent_id, order_id, amount, refunded_amount, status FROM agent_commission WHERE order_id = @order_id; |