Files
jsowell-charger-web/docs/sql/fix_online_insurance_pay_amount_20260725.sql
Guoqs 6904eae179 统一保险订单资金口径:payAmount=充电金额,明细=完整实付(充电+保险)
规则:order_basic_info.pay_amount 只记充电金额(不含保险);order_pay_record
汇总为用户完整实付(充电+保险);凭证校验以 payAmount+保险==明细 为准。

- 在线支付(OrderService.adapayCallback):payAmount 存 amount-保险(充电金额),
  回退 f11b781c2 的"完整金额"写入;下发桩额度直接用 payAmount,避免结算多退保险。
- 余额支付(Not/DelayMerchantProgramLogic.balancePayOrderV2):与在线一致,从钱包
  扣 充电+保险,order_pay_record 记完整实付,payAmount 仍只记充电金额;余额不足
  (<充电+保险)直接拒绝。会员交易流水记完整实付。
- 凭证校验(OrderBasicInfoServiceImpl.inspectOrderPayment):余额/非余额支路统一
  改为容错对比 payAmount==明细 或 payAmount+保险==明细;补偿回写只写充电金额,
  修复余额支路仍按 payAmount==明细 会误判新余额保险单的问题。
- 新增 docs/sql/fix_online_insurance_pay_amount_20260725.sql:矫正 f11b781c2 期间
  在线保险单被写成完整金额的 payAmount(dry-run/备份/回滚齐全)。
- 新增单测:calculateBalanceRefund 6 例 + inspectOrderPayment 5 例(计算层,不连库)。
2026-07-25 20:51:42 +08:00

132 lines
6.4 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.
-- ============================================================================
-- 矫正在线支付「保险订单」的 pay_amount
-- 背景:提交 f11b781c2 上线到本次回退期间,在线支付回调把 order_basic_info.pay_amount
-- 写成了「完整实付(充电+保险)」。规则应为:
-- order_basic_info.pay_amount = 充电金额(不含保险)
-- order_pay_record 汇总 = 完整实付(充电 + 保险)
-- 故需把这批订单的 pay_amount 从「完整金额」矫正为「pay_amount - insurance_amount」。
-- 适用MySQL 5.7+(未用 CTE5.7 可直接跑)
-- 日期2026-07-25
--
-- 识别口径:在线支付(微信'4'/支付宝'5') + 已支付 + insurance>0 + pay_amount≈明细汇总。
-- · 正确订单pay_amount = 明细 - 保险 < 明细
-- · 错误订单pay_amount = 明细(含保险) => 命中本脚本
--
-- ⚠️ 关于「已完成(order_status='6')」订单:结算时已用错误的 pay_amount 计算过
-- 退款(residue = pay_amount - 消费),即保险已被多退,本脚本无法追回那笔钱;
-- 对已完成订单矫正 pay_amount 只是让「记录」变正确,且会与已落库的 refund_amount
-- 不再自洽。是否处理已完成订单请按业务决定(见第 3 步的两种 WHERE
-- ============================================================================
-- ---------------------------------------------------------------------------
-- 第 1 步DRY-RUN 预览(务必先只跑这个,核对数量与金额)
-- ---------------------------------------------------------------------------
SELECT
o.order_code,
o.order_status,
CASE o.order_status WHEN '6' THEN '已完成' WHEN '4' THEN '异常' ELSE '未完成' END AS status_desc,
o.pay_mode,
o.pay_amount AS cur_pay_amount, -- 当前(错误,含保险)
o.insurance_amount AS insurance_amount,
pr.pay_record_sum AS pay_record_sum, -- 明细汇总(完整实付)
(o.pay_amount - o.insurance_amount) AS corrected_pay_amount -- 矫正后(充电金额)
FROM order_basic_info o
JOIN (
SELECT order_code, SUM(pay_amount) AS pay_record_sum
FROM order_pay_record
WHERE del_flag = '0'
GROUP BY order_code
) pr ON pr.order_code = o.order_code
WHERE o.pay_mode IN ('4','5') -- 在线支付(微信/支付宝)
AND o.pay_status = '1' -- 已支付
AND o.insurance_amount > 0 -- 有保险
AND ABS(o.pay_amount - pr.pay_record_sum) < 0.005 -- pay_amount == 明细汇总 => 含了保险(错误)
ORDER BY o.order_status, o.order_code;
-- ---------------------------------------------------------------------------
-- 第 2 步:备份受影响行(建议执行,便于回滚)
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS bak_order_pay_amount_fix_20260725 (
order_code VARCHAR(64) PRIMARY KEY,
old_pay_amount DECIMAL(10,2),
insurance_amount DECIMAL(10,2),
new_pay_amount DECIMAL(10,2),
order_status VARCHAR(4),
backup_time DATETIME
);
INSERT INTO bak_order_pay_amount_fix_20260725
(order_code, old_pay_amount, insurance_amount, new_pay_amount, order_status, backup_time)
SELECT o.order_code, o.pay_amount, o.insurance_amount,
(o.pay_amount - o.insurance_amount), o.order_status, NOW()
FROM order_basic_info o
JOIN (
SELECT order_code, SUM(pay_amount) AS pay_record_sum
FROM order_pay_record WHERE del_flag = '0' GROUP BY order_code
) pr ON pr.order_code = o.order_code
WHERE o.pay_mode IN ('4','5')
AND o.pay_status = '1'
AND o.insurance_amount > 0
AND ABS(o.pay_amount - pr.pay_record_sum) < 0.005
ON DUPLICATE KEY UPDATE backup_time = NOW();
-- ---------------------------------------------------------------------------
-- 第 3 步:正式矫正(确认第 1 步无误后执行)
-- pay_amount = pay_amount - insurance_amount = 充电金额
--
-- ▸ 方案 A推荐只矫正「未完成」订单避免动已结算/已退款的历史账
-- —— 防止这些单将来结算时多退保险。把下面这条的注释去掉执行:
-- ---------------------------------------------------------------------------
-- UPDATE order_basic_info o
-- JOIN (
-- SELECT order_code, SUM(pay_amount) AS pay_record_sum
-- FROM order_pay_record WHERE del_flag = '0' GROUP BY order_code
-- ) pr ON pr.order_code = o.order_code
-- SET o.pay_amount = o.pay_amount - o.insurance_amount
-- WHERE o.pay_mode IN ('4','5')
-- AND o.pay_status = '1'
-- AND o.insurance_amount > 0
-- AND ABS(o.pay_amount - pr.pay_record_sum) < 0.005
-- AND o.order_status NOT IN ('6','7'); -- 排除已完成/已关闭
-- ▸ 方案 B矫正「全部」受影响订单含已完成仅让记录口径一致已多退的保险无法追回
-- 如需按此执行,去掉下面这条的注释:
-- UPDATE order_basic_info o
-- JOIN (
-- SELECT order_code, SUM(pay_amount) AS pay_record_sum
-- FROM order_pay_record WHERE del_flag = '0' GROUP BY order_code
-- ) pr ON pr.order_code = o.order_code
-- SET o.pay_amount = o.pay_amount - o.insurance_amount
-- WHERE o.pay_mode IN ('4','5')
-- AND o.pay_status = '1'
-- AND o.insurance_amount > 0
-- AND ABS(o.pay_amount - pr.pay_record_sum) < 0.005;
-- ---------------------------------------------------------------------------
-- 第 4 步:矫正后复核(应为 0 行)
-- ---------------------------------------------------------------------------
-- 复核逻辑:矫正后 正确订单应满足 pay_amount + insurance = 明细汇总;
-- 若仍有 pay_amount == 明细汇总 且 insurance>0说明还有漏网的。
SELECT COUNT(*) AS still_wrong
FROM order_basic_info o
JOIN (
SELECT order_code, SUM(pay_amount) AS pay_record_sum
FROM order_pay_record WHERE del_flag = '0' GROUP BY order_code
) pr ON pr.order_code = o.order_code
WHERE o.pay_mode IN ('4','5')
AND o.pay_status = '1'
AND o.insurance_amount > 0
AND ABS(o.pay_amount - pr.pay_record_sum) < 0.005;
-- ---------------------------------------------------------------------------
-- 回滚(如需):从备份表还原
-- ---------------------------------------------------------------------------
-- UPDATE order_basic_info o
-- JOIN bak_order_pay_amount_fix_20260725 b ON b.order_code = o.order_code
-- SET o.pay_amount = b.old_pay_amount;