package com.talentcard.server.business.payment;
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.verify;
|
|
import java.util.Map;
|
|
import com.talentcard.server.business.PageResult;
|
import com.talentcard.server.business.payment.huifu.HuifuRefundGateway;
|
import com.talentcard.server.business.portal.PendingUsePortalService;
|
import com.talentcard.server.business.portal.UsageRecordPortalService;
|
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;
|
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
|
/**
|
* Seam: how the talent app reads an order that was cancelled before it was ever paid.
|
*
|
* <p>「已取消」covers two different stories and they must not be merged: an order the talent
|
* abandoned while it was still unpaid(订单取消,无需退款)and an order whose money was given back
|
* (退款处理,退款中 / 退款成功 / 退款失败). The first one never involved money, so it asks the
|
* provider nothing and reads as a plain cancellation; the refund dimension only adds a reason to the
|
* same state, it does not take the state over.
|
*/
|
@SpringBootTest
|
@ActiveProfiles("test")
|
class CancelledOrderStateTests {
|
|
@Autowired
|
private JdbcTemplate jdbc;
|
|
@Autowired
|
private UsageRecordPortalService usageRecordPortalService;
|
|
@Autowired
|
private PendingUsePortalService pendingUsePortalService;
|
|
@MockitoBean
|
private HuifuRefundGateway refundGateway;
|
|
private Long talentId;
|
private String suffix;
|
|
@BeforeEach
|
void prepareData() {
|
suffix = String.valueOf(System.nanoTime());
|
jdbc.update("insert into talent_user(openid, name, mobile, status, deleted) values (?, ?, ?, 1, 0)",
|
"cancelled-order-openid-" + suffix, "Cancelled order user", "13900000005");
|
talentId = jdbc.queryForObject("select max(id) from talent_user", Long.class);
|
}
|
|
@Test
|
void anOrderCancelledBeforePaymentIsStillListedAsCancelled() {
|
String orderNo = cancelledUnpaidOrder();
|
|
assertThat(recordOf(orderNo)).containsEntry("status", "cancelled");
|
}
|
|
@Test
|
void anOrderCancelledBeforePaymentReadsAsACancellationThatNeedsNoRefund() {
|
String orderNo = cancelledUnpaidOrder();
|
|
Map<String, Object> detail = pendingUsePortalService.cancelledDetail(orderNo, talentId);
|
|
assertThat(detail)
|
.containsEntry("cancelSource", "订单取消")
|
.containsEntry("refundStatusText", "无需退款");
|
assertThat(((Number) detail.get("actualPaidAmount")).longValue())
|
.as("没付过钱就不该显示退过钱")
|
.isZero();
|
verify(refundGateway, never()).queryRefund(any(), any());
|
}
|
|
/** An order the talent cancelled while it was still unpaid: no payment, no refund. */
|
private String cancelledUnpaidOrder() {
|
String orderNo = "CANCELLED-UNPAID-" + suffix;
|
jdbc.update("""
|
insert into benefit_order(order_no, talent_id, order_type, order_amount, base_pay_amount, pay_amount,
|
order_status, verify_status, product_name_snapshot)
|
values (?, ?, 'GROUP_BUY', 2000, 2000, 2000, 2, 0, '取消测试订单')
|
""", orderNo, talentId);
|
return orderNo;
|
}
|
|
private Map<String, Object> recordOf(String orderNo) {
|
PageResult<Map<String, Object>> page = usageRecordPortalService.list(talentId, null, 1, 50);
|
return page.records().stream()
|
.filter(record -> orderNo.equals(String.valueOf(record.get("businessId"))))
|
.findFirst()
|
.orElseThrow(() -> new AssertionError("Order " + orderNo + " is missing from the talent order list"));
|
}
|
}
|