package com.talentcard.server.business.merchant;
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
|
import java.sql.Timestamp;
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
|
import com.talentcard.server.business.merchant.auth.MerchantPrincipal;
|
import com.talentcard.server.common.BusinessException;
|
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.Test;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.datasource.DriverManagerDataSource;
|
|
class MerchantVerificationFreeStayTests {
|
|
private JdbcTemplate jdbc;
|
private MerchantVerificationService service;
|
private MerchantPrincipal principal;
|
|
@BeforeEach
|
void setUp() {
|
DriverManagerDataSource dataSource = new DriverManagerDataSource(
|
"jdbc:h2:mem:free-stay-verification-" + System.nanoTime()
|
+ ";MODE=MySQL;DATABASE_TO_LOWER=TRUE;DB_CLOSE_DELAY=-1", "sa", "");
|
jdbc = new JdbcTemplate(dataSource);
|
jdbc.execute("""
|
create table merchant_verification_attempt (
|
id bigint auto_increment primary key,
|
verification_id varchar(64) not null,
|
credential_type varchar(32) not null,
|
dynamic_code_id bigint,
|
order_verification_code_id bigint,
|
free_stay_verification_code_id bigint,
|
merchant_id bigint not null,
|
store_id bigint not null,
|
operator_id bigint not null,
|
expires_at timestamp,
|
status tinyint not null default 0,
|
verify_no varchar(64),
|
confirmed_at timestamp
|
)
|
""");
|
jdbc.execute("""
|
create table talent_free_stay_voucher (
|
id bigint primary key,
|
talent_id bigint not null,
|
merchant_id bigint not null,
|
card_id bigint not null,
|
total_count int not null,
|
used_count int not null,
|
valid_end date not null,
|
status tinyint not null
|
)
|
""");
|
jdbc.execute("""
|
create table free_stay_verification_code (
|
id bigint primary key,
|
voucher_id bigint not null,
|
code_token varchar(128),
|
expires_at timestamp not null,
|
status tinyint not null
|
)
|
""");
|
jdbc.execute("""
|
create table talent_user (
|
id bigint primary key,
|
name varchar(64),
|
mobile varchar(32),
|
status tinyint not null default 1,
|
source_area_type varchar(32),
|
deleted tinyint not null default 0
|
)
|
""");
|
jdbc.execute("""
|
create table talent_card (
|
id bigint primary key,
|
card_level varchar(64),
|
card_no varchar(64),
|
status tinyint not null default 1,
|
valid_end date,
|
deleted tinyint not null default 0
|
)
|
""");
|
jdbc.execute("""
|
create table talent_dynamic_code (
|
id bigint primary key,
|
expected_merchant_id bigint,
|
expires_at timestamp,
|
card_id bigint,
|
talent_id bigint,
|
code_token varchar(128),
|
status tinyint not null default 0
|
)
|
""");
|
jdbc.execute("""
|
create table benefit_order (
|
id bigint primary key,
|
order_no varchar(64),
|
order_status tinyint,
|
verify_status tinyint,
|
merchant_id bigint,
|
store_id bigint,
|
product_name_snapshot varchar(255),
|
benefit_id bigint,
|
talent_id bigint,
|
card_id bigint,
|
deleted tinyint not null default 0
|
)
|
""");
|
jdbc.execute("create table benefit (id bigint primary key, benefit_name varchar(255))");
|
jdbc.execute("""
|
create table order_dynamic_verification_code (
|
id bigint primary key,
|
order_id bigint,
|
code_token varchar(128),
|
expires_at timestamp,
|
status tinyint not null default 0
|
)
|
""");
|
jdbc.execute("""
|
create table verify_record (
|
id bigint auto_increment primary key,
|
verify_no varchar(64) not null,
|
order_id bigint,
|
talent_id bigint,
|
card_id bigint,
|
benefit_id bigint,
|
merchant_id bigint,
|
store_id bigint,
|
staff_id bigint,
|
verify_type varchar(32),
|
verify_amount bigint,
|
subsidy_amount bigint,
|
remark varchar(255)
|
)
|
""");
|
jdbc.execute("create table admin_setting (setting_key varchar(64) primary key, setting_value varchar(1000))");
|
jdbc.update("insert into admin_setting(setting_key, setting_value) values ('hotelSubsidy', '268.50')");
|
jdbc.execute("create table payment_transaction (id bigint primary key)");
|
// 这些用例只验证核销本身,资金确认用替身:确认逻辑由 OrderVerificationService 的用例覆盖。
|
service = new MerchantVerificationService(jdbc, new ManualConsumptionService(jdbc, null),
|
org.mockito.Mockito.mock(com.talentcard.server.business.order.OrderVerificationService.class),
|
new MerchantNumericCodeAttemptGuard(jdbc));
|
principal = new MerchantPrincipal(9L, 88L, 7L, "operator");
|
}
|
|
@Test
|
void confirmsFreeStayAfterScannedCodeExpiresAndRefreshes() {
|
insertFreeStay("free-stay-expired-code", LocalDate.now().plusDays(1));
|
|
MapResult confirmed = confirm("free-stay-expired-code");
|
|
assertThat(confirmed.verified()).isTrue();
|
assertThat(jdbc.queryForObject("select status from free_stay_verification_code where id = 30", Integer.class)).isEqualTo(1);
|
assertThat(jdbc.queryForObject("select used_count from talent_free_stay_voucher where id = 20", Integer.class)).isEqualTo(1);
|
assertThat(jdbc.queryForObject("select status from merchant_verification_attempt where verification_id = ?", Integer.class,
|
"free-stay-expired-code")).isEqualTo(1);
|
assertThat(jdbc.queryForObject("select count(*) from verify_record where verify_type = 'HOTEL_FREE'", Integer.class)).isEqualTo(1);
|
assertThat(jdbc.queryForObject("select subsidy_amount from verify_record where verify_type = 'HOTEL_FREE'", Long.class)).isEqualTo(26850L);
|
assertThat(jdbc.queryForObject("select count(*) from benefit_order", Integer.class)).isZero();
|
assertThat(jdbc.queryForObject("select count(*) from payment_transaction", Integer.class)).isZero();
|
}
|
|
@Test
|
void scanReturnsConfiguredHotelSubsidyAmount() {
|
jdbc.update("insert into talent_card(id, card_level, deleted) values (10, '卓越人才卡', 0)");
|
jdbc.update("insert into talent_user(id, name, mobile, deleted) values (1015, '测试人才', '13800000000', 0)");
|
jdbc.update("""
|
insert into talent_free_stay_voucher(id, talent_id, merchant_id, card_id, total_count, used_count, valid_end, status)
|
values (20, 1015, 88, 10, 2, 0, ?, 1)
|
""", LocalDate.now().plusDays(1));
|
jdbc.update("""
|
insert into free_stay_verification_code(id, voucher_id, code_token, expires_at, status)
|
values (30, 20, 'free-stay-scan-code', ?, 0)
|
""", Timestamp.valueOf(LocalDateTime.now().plusMinutes(1)));
|
|
java.util.Map<String, Object> scanned = service.scan(principal, new VerificationScanRequest("free-stay-scan-code"));
|
|
assertThat(scanned.get("credentialType")).isEqualTo("FREE_STAY_CODE");
|
assertThat(scanned.get("subsidyAmount")).isEqualTo(26850L);
|
}
|
|
@Test
|
void stillRejectsExpiredFreeStayVoucher() {
|
insertFreeStay("free-stay-expired-voucher", LocalDate.now().minusDays(1));
|
|
assertThatThrownBy(() -> confirm("free-stay-expired-voucher"))
|
.isInstanceOf(BusinessException.class)
|
.hasMessageContaining("当前不可核销");
|
}
|
|
private void insertFreeStay(String verificationId, LocalDate voucherValidEnd) {
|
jdbc.update("insert into talent_card(id, card_level, deleted) values (10, '卓越人才卡', 0)");
|
jdbc.update("""
|
insert into talent_free_stay_voucher(id, talent_id, merchant_id, card_id, total_count, used_count, valid_end, status)
|
values (20, 1015, 88, 10, 2, 0, ?, 1)
|
""", voucherValidEnd);
|
jdbc.update("""
|
insert into free_stay_verification_code(id, voucher_id, code_token, expires_at, status)
|
values (30, 20, ?, ?, 2)
|
""", verificationId, Timestamp.valueOf(LocalDateTime.now().minusMinutes(1)));
|
jdbc.update("""
|
insert into merchant_verification_attempt(verification_id, credential_type, free_stay_verification_code_id,
|
merchant_id, store_id, operator_id, expires_at, status)
|
values (?, 'FREE_STAY_CODE', 30, 88, 7, 9, ?, 0)
|
""", verificationId, Timestamp.valueOf(LocalDateTime.now().minusMinutes(1)));
|
}
|
|
private MapResult confirm(String verificationId) {
|
java.util.Map<String, Object> result = service.confirmFreeStay(principal, new VerificationConfirmRequest(verificationId),
|
"free-stay-confirm-" + verificationId);
|
return new MapResult(Boolean.TRUE.equals(result.get("verified")));
|
}
|
|
private record MapResult(boolean verified) {}
|
}
|