From ec8e8f1c208abe54042add2124c9759d9b1369d1 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期六, 12 九月 2026 16:59:06 +0800
Subject: [PATCH] feat(order): 核销幂等与退款边界按订单串行化

---
 talent-card-server/src/test/java/com/talentcard/server/business/merchant/MerchantVerificationOrderCodeTests.java |   34 ++++++++++++++++++++++++++++------
 1 files changed, 28 insertions(+), 6 deletions(-)

diff --git a/talent-card-server/src/test/java/com/talentcard/server/business/merchant/MerchantVerificationOrderCodeTests.java b/talent-card-server/src/test/java/com/talentcard/server/business/merchant/MerchantVerificationOrderCodeTests.java
index 9311959..efdc50f 100644
--- a/talent-card-server/src/test/java/com/talentcard/server/business/merchant/MerchantVerificationOrderCodeTests.java
+++ b/talent-card-server/src/test/java/com/talentcard/server/business/merchant/MerchantVerificationOrderCodeTests.java
@@ -12,11 +12,14 @@
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
 import org.springframework.jdbc.datasource.DriverManagerDataSource;
+import org.springframework.transaction.support.TransactionTemplate;
 
 class MerchantVerificationOrderCodeTests {
 
     private JdbcTemplate jdbc;
+    private TransactionTemplate transactions;
     private MerchantVerificationService service;
     private MerchantPrincipal principal;
 
@@ -26,6 +29,7 @@
                 "jdbc:h2:mem:order-code-verification-" + System.nanoTime()
                         + ";MODE=MySQL;DATABASE_TO_LOWER=TRUE;DB_CLOSE_DELAY=-1", "sa", "");
         jdbc = new JdbcTemplate(dataSource);
+        transactions = new TransactionTemplate(new DataSourceTransactionManager(dataSource));
         jdbc.execute("""
                 create table merchant_verification_attempt (
                     id bigint auto_increment primary key,
@@ -55,6 +59,8 @@
                     pay_amount bigint not null,
                     order_status tinyint not null,
                     verify_status tinyint not null,
+                    refund_status tinyint not null default 0,
+                    withdrawal_status tinyint not null default 0,
                     merchant_id bigint not null,
                     store_id bigint,
                     deleted tinyint not null default 0
@@ -87,7 +93,8 @@
                     remark varchar(255)
                 )
                 """);
-        service = new MerchantVerificationService(jdbc, new ManualConsumptionService(jdbc, null));
+        service = new MerchantVerificationService(jdbc, new ManualConsumptionService(jdbc, null),
+                new com.talentcard.server.business.order.OrderFundBoundaryService(jdbc));
         principal = new MerchantPrincipal(9L, 88L, 7L, "operator");
     }
 
@@ -95,8 +102,7 @@
     void confirmsGroupOrderAfterScannedCodeExpiresAndRefreshes() {
         insertOrder("expired-group-order", 1);
 
-        Map<String, Object> confirmed = service.confirm(principal,
-                new VerificationConfirmRequest("expired-group-order"), "order-confirm-expired-group-order");
+        Map<String, Object> confirmed = confirm("expired-group-order");
 
         assertThat(confirmed.get("verified")).isEqualTo(true);
         assertThat(jdbc.queryForObject("select status from order_dynamic_verification_code where id = 30", Integer.class)).isEqualTo(1);
@@ -110,10 +116,26 @@
     void stillRejectsUnpaidGroupOrder() {
         insertOrder("unpaid-group-order", 0);
 
-        assertThatThrownBy(() -> service.confirm(principal, new VerificationConfirmRequest("unpaid-group-order"),
-                "order-confirm-unpaid-group-order"))
+        assertThatThrownBy(() -> confirm("unpaid-group-order"))
                 .isInstanceOf(BusinessException.class)
-                .hasMessageContaining("当前不可核销");
+                .hasMessageContaining("未支付");
+    }
+
+    @Test
+    void rejectsGroupOrderThatWasRefundedWhileTheCodeWasOpen() {
+        insertOrder("refunded-group-order", 1);
+        jdbc.update("update benefit_order set refund_status = 2 where id = 20");
+
+        assertThatThrownBy(() -> confirm("refunded-group-order"))
+                .isInstanceOf(BusinessException.class)
+                .hasMessageContaining("退款");
+        assertThat(jdbc.queryForObject("select verify_status from benefit_order where id = 20", Integer.class)).isZero();
+    }
+
+    /** 核销确认按订单行锁串行化,所以测试也必须在一个真实事务里调用它。 */
+    private Map<String, Object> confirm(String verificationId) {
+        return transactions.execute(status -> service.confirm(principal,
+                new VerificationConfirmRequest(verificationId), "order-confirm-" + verificationId));
     }
 
     private void insertOrder(String verificationId, int orderStatus) {

--
Gitblit v1.7.1