package com.talentcard.server.business.merchant;
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
import java.sql.Timestamp;
|
import java.time.LocalDateTime;
|
import java.util.Map;
|
|
import com.talentcard.server.business.PageResult;
|
import com.talentcard.server.business.merchant.auth.MerchantPrincipal;
|
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.Test;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.test.context.ActiveProfiles;
|
|
@SpringBootTest
|
@ActiveProfiles("test")
|
class MerchantWorkServiceTests {
|
|
@Autowired
|
private JdbcTemplate jdbc;
|
|
@Autowired
|
private MerchantWorkService merchantWorkService;
|
|
private Long merchantId;
|
private Long storeId;
|
private Long talentId;
|
private MerchantPrincipal principal;
|
|
@BeforeEach
|
void prepareOrders() {
|
String suffix = String.valueOf(System.nanoTime());
|
jdbc.update("insert into merchant(merchant_name, status, can_login_merchant_app, deleted) values (?, 1, 1, 0)", "Order test merchant " + suffix);
|
merchantId = jdbc.queryForObject("select max(id) from merchant", Long.class);
|
jdbc.update("insert into merchant_store(merchant_id, store_name, status, deleted) values (?, ?, 1, 0)", merchantId, "Order test store");
|
storeId = jdbc.queryForObject("select max(id) from merchant_store", Long.class);
|
jdbc.update("insert into talent_user(openid, name, mobile, status, deleted) values (?, ?, ?, 1, 0)", "order-test-" + suffix, "订单测试用户", "13800000000");
|
talentId = jdbc.queryForObject("select max(id) from talent_user", Long.class);
|
principal = new MerchantPrincipal(9001L, merchantId, storeId, "operator");
|
insertOrder("GROUP_BUY", 1, 0, LocalDateTime.of(2026, 7, 10, 9, 0));
|
insertOrder("GROUP_BUY", 1, 1, LocalDateTime.of(2026, 7, 11, 9, 0));
|
insertOrder("STORE_CONSUME", 0, 0, LocalDateTime.of(2026, 7, 12, 9, 0));
|
insertOrder("FREE_STAY", 1, 1, LocalDateTime.of(2026, 7, 13, 9, 0));
|
}
|
|
@Test
|
void filtersOrdersByCategoryStatusAndDateWithoutExposingFullMobile() {
|
PageResult<Map<String, Object>> groupPending = merchantWorkService.orders(merchantId, storeId, 1, 10,
|
new MerchantOrderQuery("group", "pending_verification", "2026-07-10", "2026-07-10"));
|
PageResult<Map<String, Object>> storePending = merchantWorkService.orders(merchantId, storeId, 1, 10,
|
new MerchantOrderQuery("store", "pending_payment", null, null));
|
PageResult<Map<String, Object>> stays = merchantWorkService.orders(merchantId, storeId, 1, 10,
|
new MerchantOrderQuery("stay", "all", null, null));
|
|
assertThat(groupPending.total()).isEqualTo(1);
|
assertThat(groupPending.records().get(0)).containsEntry("category", "group").containsEntry("status", "pending_verification");
|
assertThat(String.valueOf(groupPending.records().get(0).get("customerMobile"))).isEqualTo("138****0000");
|
assertThat(storePending.total()).isEqualTo(1);
|
assertThat(storePending.records().get(0)).containsEntry("category", "store").containsEntry("status", "pending_payment");
|
assertThat(stays.total()).isEqualTo(1);
|
assertThat(stays.records().get(0)).containsEntry("category", "stay").containsEntry("status", "completed");
|
}
|
|
@Test
|
void verifiesAndRevokesGroupBuyIdempotently() {
|
String orderNo = insertOrder("GROUP_BUY", 1, 0, LocalDateTime.now());
|
Long orderId = jdbc.queryForObject("select id from benefit_order where order_no = ?", Long.class, orderNo);
|
|
Map<String, Object> verified = merchantWorkService.verify(orderNo, principal,
|
new VerifyRequest("merchant", "test verification"), "verify-" + orderNo);
|
Map<String, Object> repeatedVerification = merchantWorkService.verify(orderNo, principal,
|
new VerifyRequest("merchant", "test verification"), "verify-" + orderNo);
|
|
assertThat(verified).containsEntry("status", "completed");
|
assertThat(repeatedVerification).containsEntry("status", "completed");
|
assertThat(jdbc.queryForObject("select count(*) from verify_record where order_id = ?", Integer.class, orderId)).isEqualTo(1);
|
|
String revokeKey = "revoke-" + orderNo;
|
Map<String, Object> revoked = merchantWorkService.revokeVerification(principal,
|
new MerchantRevokeVerificationRequest(String.valueOf(orderId), "test reversal"), revokeKey);
|
Map<String, Object> repeatedRevoke = merchantWorkService.revokeVerification(principal,
|
new MerchantRevokeVerificationRequest(String.valueOf(orderId), "test reversal"), revokeKey);
|
|
assertThat(revoked).containsEntry("orderStatus", "pending_verification");
|
assertThat(repeatedRevoke).containsEntry("orderStatus", "pending_verification");
|
assertThat(jdbc.queryForObject("select verify_status from benefit_order where id = ?", Integer.class, orderId)).isZero();
|
assertThat(jdbc.queryForObject("select status from verify_record where order_id = ?", Integer.class, orderId)).isEqualTo(2);
|
}
|
|
@Test
|
void derivesConsistentDiscountBreakdownForLegacyOrderSnapshots() {
|
String orderNo = "LEGACY-" + System.nanoTime();
|
jdbc.update("""
|
insert into benefit_order(order_no, talent_id, merchant_id, store_id, order_type, order_amount,
|
subsidy_amount, base_pay_amount, coupon_amount, pay_amount,
|
order_status, verify_status, deleted)
|
values (?, ?, ?, ?, 'GROUP_BUY', 14800, 0, 14800, 0, 1, 1, 0, 0)
|
""", orderNo, talentId, merchantId, storeId);
|
|
Map<String, Object> detail = merchantWorkService.order(orderNo, merchantId, storeId);
|
|
assertThat(detail).containsEntry("talentDiscountAmount", 14799L)
|
.containsEntry("couponAmount", 0L)
|
.containsEntry("subsidyAmount", 14799L);
|
}
|
|
@Test
|
void aCancelledOrderIsShownAsCancelledInsteadOfWaitingToBeVerified() {
|
String orderNo = insertOrder("GROUP_BUY", 2, 0, LocalDateTime.now());
|
|
assertThat(recordOf(list("group", "all"), orderNo))
|
.containsEntry("status", "cancelled")
|
.containsEntry("statusText", "已取消");
|
assertThat(orderNos(list("group", "pending_verification")))
|
.as("已取消的订单不是待核销:商户照它去核销只会被资金边界拒绝")
|
.doesNotContain(orderNo);
|
assertThat(orderNos(list("group", "closed"))).contains(orderNo);
|
}
|
|
@Test
|
void aRefundedOrderIsShownAsRefundedAndStopsWaitingToBeVerified() {
|
String orderNo = insertOrder("GROUP_BUY", 1, 0, LocalDateTime.now());
|
jdbc.update("update benefit_order set refund_status = 2 where order_no = ?", orderNo);
|
|
assertThat(recordOf(list("group", "all"), orderNo))
|
.containsEntry("status", "refunded")
|
.containsEntry("statusText", "已退款");
|
assertThat(orderNos(list("group", "pending_verification"))).doesNotContain(orderNo);
|
assertThat(orderNos(list("group", "closed"))).contains(orderNo);
|
}
|
|
@Test
|
void aRefundInFlightIsShownAsRefunding() {
|
String orderNo = insertOrder("GROUP_BUY", 1, 0, LocalDateTime.now());
|
jdbc.update("update benefit_order set refund_status = 1 where order_no = ?", orderNo);
|
|
assertThat(recordOf(list("group", "all"), orderNo)).containsEntry("statusText", "退款中");
|
assertThat(orderNos(list("group", "pending_verification"))).doesNotContain(orderNo);
|
}
|
|
@Test
|
void theOrderDetailCarriesTheSameClosedState() {
|
String orderNo = insertOrder("GROUP_BUY", 1, 0, LocalDateTime.now());
|
jdbc.update("update benefit_order set refund_status = 1 where order_no = ?", orderNo);
|
|
assertThat(merchantWorkService.order(orderNo, merchantId, storeId))
|
.containsEntry("status", "refunded")
|
.containsEntry("statusText", "退款中");
|
assertThat(merchantWorkService.order(insertOrder("GROUP_BUY", 2, 0, LocalDateTime.now()), merchantId, storeId))
|
.containsEntry("status", "cancelled");
|
}
|
|
private PageResult<Map<String, Object>> list(String category, String status) {
|
return merchantWorkService.orders(merchantId, storeId, 1, 50, new MerchantOrderQuery(category, status, null, null));
|
}
|
|
private Map<String, Object> recordOf(PageResult<Map<String, Object>> page, String orderNo) {
|
return page.records().stream()
|
.filter(record -> orderNo.equals(String.valueOf(record.get("orderNo"))))
|
.findFirst()
|
.orElseThrow(() -> new AssertionError("Order " + orderNo + " is missing from the merchant order list"));
|
}
|
|
private java.util.List<String> orderNos(PageResult<Map<String, Object>> page) {
|
return page.records().stream().map(record -> String.valueOf(record.get("orderNo"))).toList();
|
}
|
|
private String insertOrder(String type, int orderStatus, int verifyStatus, LocalDateTime createdAt) {
|
String orderNo = "ORDER-" + System.nanoTime();
|
jdbc.update("""
|
insert into benefit_order(order_no, talent_id, merchant_id, store_id, order_type, order_amount,
|
subsidy_amount, pay_amount, order_status, verify_status, created_at, deleted)
|
values (?, ?, ?, ?, ?, 10000, 1000, 9000, ?, ?, ?, 0)
|
""", orderNo, talentId, merchantId, storeId, type, orderStatus, verifyStatus,
|
Timestamp.valueOf(createdAt));
|
return orderNo;
|
}
|
}
|