|
@@ -0,0 +1,69 @@
|
|
|
|
|
+-- ====================================================
|
|
|
|
|
+-- v20: 资金关键表唯一索引(幂等兜底)
|
|
|
|
|
+-- 目的:为支付回调 / 退款回调 / 订单结算提供数据库层幂等兜底,
|
|
|
|
|
+-- 防止并发回调、消息重投导致重复入账 / 重复扣减 / 重复分账。
|
|
|
|
|
+-- 涉及表:t_pay_log、t_wallet_detail、t_split_record
|
|
|
|
|
+--
|
|
|
|
|
+-- 执行前提(重要):
|
|
|
|
|
+-- 1. 生产执行前先运行下方"查重"SQL,确认无重复数据;
|
|
|
|
|
+-- 若存在重复,需先人工清理后再执行本脚本(否则建索引会失败)。
|
|
|
|
|
+-- 2. 建议在业务低峰期执行(大表加唯一索引会锁表/耗时)。
|
|
|
|
|
+-- ====================================================
|
|
|
|
|
+
|
|
|
|
|
+-- ----------------------------
|
|
|
|
|
+-- 0. 查重检查(执行前先跑,以下 3 条应全部返回 0 行或空集)
|
|
|
|
|
+-- ----------------------------
|
|
|
|
|
+-- SELECT out_trade_no, COUNT(*) FROM t_pay_log GROUP BY out_trade_no HAVING COUNT(*) > 1;
|
|
|
|
|
+-- SELECT order_no, type, COUNT(*) FROM t_wallet_detail GROUP BY order_no, type HAVING COUNT(*) > 1;
|
|
|
|
|
+-- SELECT trade_no, type, from_station_id, to_station_id, COUNT(*) FROM t_split_record
|
|
|
|
|
+-- GROUP BY trade_no, type, from_station_id, to_station_id HAVING COUNT(*) > 1;
|
|
|
|
|
+
|
|
|
|
|
+-- ----------------------------
|
|
|
|
|
+-- 1. 幂等建唯一索引
|
|
|
|
|
+-- ----------------------------
|
|
|
|
|
+DROP PROCEDURE IF EXISTS add_finance_idempotency_indexes;
|
|
|
|
|
+DELIMITER //
|
|
|
|
|
+CREATE PROCEDURE add_finance_idempotency_indexes()
|
|
|
|
|
+BEGIN
|
|
|
|
|
+ -- t_pay_log.out_trade_no:微信商户订单号唯一,同一笔支付只允许处理一次
|
|
|
|
|
+ IF NOT EXISTS (
|
|
|
|
|
+ SELECT 1 FROM information_schema.STATISTICS
|
|
|
|
|
+ WHERE TABLE_SCHEMA = DATABASE()
|
|
|
|
|
+ AND TABLE_NAME = 't_pay_log'
|
|
|
|
|
+ AND INDEX_NAME = 'uk_out_trade_no'
|
|
|
|
|
+ ) THEN
|
|
|
|
|
+ ALTER TABLE `t_pay_log` ADD UNIQUE KEY `uk_out_trade_no` (`out_trade_no`);
|
|
|
|
|
+ END IF;
|
|
|
|
|
+
|
|
|
|
|
+ -- t_wallet_detail(order_no, type):同一订单号同类型的钱包流水唯一
|
|
|
|
|
+ IF NOT EXISTS (
|
|
|
|
|
+ SELECT 1 FROM information_schema.STATISTICS
|
|
|
|
|
+ WHERE TABLE_SCHEMA = DATABASE()
|
|
|
|
|
+ AND TABLE_NAME = 't_wallet_detail'
|
|
|
|
|
+ AND INDEX_NAME = 'uk_order_no_type'
|
|
|
|
|
+ ) THEN
|
|
|
|
|
+ ALTER TABLE `t_wallet_detail` ADD UNIQUE KEY `uk_order_no_type` (`order_no`, `type`);
|
|
|
|
|
+ END IF;
|
|
|
|
|
+
|
|
|
|
|
+ -- t_split_record(trade_no, type, from_station_id, to_station_id):
|
|
|
|
|
+ -- 同一交易同类型同流向的分账记录唯一(跨店 EXPEND/INCOME 两条 type 不同,互不冲突)
|
|
|
|
|
+ IF NOT EXISTS (
|
|
|
|
|
+ SELECT 1 FROM information_schema.STATISTICS
|
|
|
|
|
+ WHERE TABLE_SCHEMA = DATABASE()
|
|
|
|
|
+ AND TABLE_NAME = 't_split_record'
|
|
|
|
|
+ AND INDEX_NAME = 'uk_trade_no_type_from_to'
|
|
|
|
|
+ ) THEN
|
|
|
|
|
+ ALTER TABLE `t_split_record`
|
|
|
|
|
+ ADD UNIQUE KEY `uk_trade_no_type_from_to` (`trade_no`, `type`, `from_station_id`, `to_station_id`);
|
|
|
|
|
+ END IF;
|
|
|
|
|
+END //
|
|
|
|
|
+DELIMITER ;
|
|
|
|
|
+CALL add_finance_idempotency_indexes();
|
|
|
|
|
+DROP PROCEDURE IF EXISTS add_finance_idempotency_indexes;
|
|
|
|
|
+
|
|
|
|
|
+-- ====================================================
|
|
|
|
|
+-- 验证:
|
|
|
|
|
+-- SHOW INDEX FROM t_pay_log WHERE Key_name = 'uk_out_trade_no';
|
|
|
|
|
+-- SHOW INDEX FROM t_wallet_detail WHERE Key_name = 'uk_order_no_type';
|
|
|
|
|
+-- SHOW INDEX FROM t_split_record WHERE Key_name = 'uk_trade_no_type_from_to';
|
|
|
|
|
+-- ====================================================
|