package com.talentcard.server.business.portal; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.lang.reflect.Method; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.ArgumentCaptor; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.jdbc.core.JdbcTemplate; @ExtendWith(MockitoExtension.class) class PortalServiceTests { @Mock private JdbcTemplate jdbc; @InjectMocks private PortalService service; @Test void cinemaMerchantOnlyShowsCouponTagWhenAnActiveCouponExists() { when(jdbc.queryForList(anyString(), any(Object[].class))).thenReturn(List.of( Map.of("id", 1L, "merchant_name", "有券影院", "merchant_type", "cinema", "status", 1, "coupon_available", 1), Map.of("id", 2L, "merchant_name", "无券影院", "merchant_type", "cinema", "status", 1, "coupon_available", 0))); when(jdbc.queryForObject(anyString(), eq(Long.class), any(Object[].class))).thenReturn(2L); var result = service.entertainmentMerchants(1, 4); assertThat(result.records().get(0)) .containsEntry("couponAvailable", true) .containsEntry("discountText", "券"); assertThat(result.records().get(1)) .containsEntry("couponAvailable", false) .containsEntry("discountText", ""); } @Test void shoppingMerchantUsesConfiguredStoreDiscountInsteadOfRightsTag() throws Exception { Method method = PortalService.class.getDeclaredMethod("merchantRightDiscountText", Map.class); method.setAccessible(true); String discount = (String) method.invoke(service, Map.of( "enabled", true, "tag", "卓越人才到店消费可用", "storeConsume", true, "consumeEnabled", true, "discount", "8.50")); assertThat(discount).isEqualTo("8.5折"); } @Test void carWashListHidesMiniProgramTagAndShowsNormalConfiguredDiscount() throws Exception { Method method = PortalService.class.getDeclaredMethod("carWashListDiscountText", String.class, Map.class); method.setAccessible(true); Map right = Map.of( "enabled", true, "storeConsume", true, "consumeEnabled", true, "discount", "8.50"); assertThat(method.invoke(service, "miniProgram", right)).isEqualTo(""); assertThat(method.invoke(service, "normal", right)).isEqualTo("8.5折"); } @Test @SuppressWarnings("unchecked") void recommendedMerchantTagsFollowCategoryListRules() throws Exception { Method method = PortalService.class.getDeclaredMethod( "merchantListBenefitTags", Map.class, Map.class, Long.class); method.setAccessible(true); List foodTags = (List) method.invoke(service, Map.of("merchantType", "food", "couponAvailable", true, "discountText", "8.5折"), Map.of(), null); List cinemaTags = (List) method.invoke(service, Map.of("merchantType", "cinema", "couponAvailable", false, "discountText", "7折"), Map.of(), null); assertThat(foodTags).containsExactly("券", "8.5折"); assertThat(cinemaTags).isEmpty(); for (String type : List.of("employment", "finance", "approval", "apartment", "fuel", "housing", "car", "appliance", "digital", "telecom")) { List tags = (List) method.invoke(service, Map.of("merchantType", type, "couponAvailable", true, "discountText", "7折"), Map.of("serviceLabel", "人才专享"), null); assertThat(tags).as(type).isEmpty(); } List scenicTags = (List) method.invoke(service, Map.of("merchantType", "scenic", "couponAvailable", true, "discountText", "8.8折"), Map.of(), null); List cultureTags = (List) method.invoke(service, Map.of("merchantType", "culture", "couponAvailable", true, "discountText", ""), Map.of(), null); assertThat(scenicTags).containsExactly("券", "8.8折"); assertThat(cultureTags).containsExactly("券"); } @Test @SuppressWarnings("unchecked") void merchantDetailHeaderTagsDriveListTags() throws Exception { Method method = PortalService.class.getDeclaredMethod( "merchantBenefitTags", Long.class, String.class, String.class, boolean.class, String.class, Map.class); method.setAccessible(true); List foodTags = (List) method.invoke(service, null, null, "food", true, "8.8折", null); List hotelTags = (List) method.invoke(service, null, null, "hotel", true, "7折", Map.of( "freeVoucherEnabled", true, "freeVoucherTimes", "2", "storeConsume", true, "consumeEnabled", true, "consumeType", "折扣", "consumeDiscount", "8.5")); assertThat(foodTags).containsExactly("券", "8.8折"); // 酒店折扣同样取「团单折扣 7 折」与「商家权益到店消费折扣 8.5 折」中更低的一档。 assertThat(hotelTags).containsExactly("券", "免费2次", "7.0折"); } @Test void productFreeRightsProduceFreeTimesTag() throws Exception { when(jdbc.queryForList(anyString(), any(Object[].class))).thenReturn(List.of( Map.of("rightsConfig", "[{\"level\": \"卓越人才卡\", \"enabled\": true, \"free\": true," + " \"limitCount\": 10, \"exceedDiscount\": 7}]"), Map.of("rightsConfig", "[{\"level\": \"卓越人才卡\", \"enabled\": true, \"free\": true," + " \"limitCount\": 1, \"exceedDiscount\": 6}]"))); Method method = PortalService.class.getDeclaredMethod("productFreeTimesTag", Long.class, String.class); method.setAccessible(true); assertThat((String) method.invoke(service, 83L, "卓越人才卡")).isEqualTo("免费10次"); assertThat((String) method.invoke(service, 83L, "杰出人才卡")).isEmpty(); } @Test @SuppressWarnings("unchecked") void parkingTagsUseFreeForFreeRightsAndDiscountForPaidRights() throws Exception { when(jdbc.queryForList(anyString(), any(Object[].class))).thenReturn(List.of()); when(jdbc.queryForObject(anyString(), eq(Long.class), any(Object[].class))).thenReturn(0L); Method method = PortalService.class.getDeclaredMethod( "parkingBenefitTags", Long.class, List.class, String.class, String.class); method.setAccessible(true); List freeTags = (List) method.invoke(service, 1L, List.of(Map.of( "level", "卓越人才卡", "enabled", true, "parkingType", "free")), "卓越人才卡", "LOCAL"); List paidTags = (List) method.invoke(service, 1L, List.of(Map.of( "level", "卓越人才卡", "enabled", true, "parkingType", "paid", "storeConsume", true, "consumeEnabled", false, "discount", "8.50")), "卓越人才卡", "LOCAL"); assertThat(freeTags).containsExactly("免费"); assertThat(paidTags).containsExactly("8.5折"); } @Test @SuppressWarnings("unchecked") void merchantRightMatchingKeepsLocalAndOutsideRightsApart() throws Exception { Method method = PortalService.class.getDeclaredMethod( "matchingMerchantRight", List.class, String.class, String.class, boolean.class); method.setAccessible(true); List> rights = List.of( Map.of("level", "卓越人才卡(外地)", "tag", "卓越人才卡(外地)", "detail", "外地权益", "enabled", true), Map.of("level", "卓越人才卡(本地)", "tag", "卓越人才卡(本地)", "detail", "本地权益", "enabled", true)); Map local = (Map) method.invoke(service, rights, "卓越人才卡", "LOCAL", true); Map outside = (Map) method.invoke(service, rights, "卓越人才卡", "OUTSIDE", true); assertThat(local.get("detail")).isEqualTo("本地权益"); assertThat(outside.get("detail")).isEqualTo("外地权益"); } @Test void merchantRightMatchingNeverFallsBackToAnotherAreaScope() throws Exception { Method method = PortalService.class.getDeclaredMethod( "matchingMerchantRight", List.class, String.class, String.class, boolean.class); method.setAccessible(true); // 只配了“(外地)”时,本地人才拿不到这份权益;不带地区后缀的配置两边都能用。 List> outsideOnly = List.of( Map.of("level", "卓越人才卡(外地)", "detail", "外地权益", "enabled", true)); List> plain = List.of(Map.of("level", "卓越人才卡", "detail", "通用权益", "enabled", true)); assertThat(method.invoke(service, outsideOnly, "卓越人才卡", "LOCAL", true)).isNull(); assertThat(method.invoke(service, plain, "卓越人才卡", "LOCAL", true)).isNotNull(); assertThat(method.invoke(service, plain, "卓越人才卡", "OUTSIDE", true)).isNotNull(); } @Test void newsListSortsPinnedArticlesByModifiedTime() { when(jdbc.queryForObject(anyString(), eq(Long.class), any(Object[].class))).thenReturn(0L); when(jdbc.queryForList(anyString(), any(Object[].class))).thenReturn(List.of()); service.news(1, 10); ArgumentCaptor sql = ArgumentCaptor.forClass(String.class); verify(jdbc).queryForList(sql.capture(), any(Object[].class)); assertThat(sql.getValue()) .contains("from news_article") .contains("is_top desc") .contains("case when is_top = 1 then updated_at end desc"); } @Test void premiumListTagComesFromMerchantConfigurationOnly() { // 商家权益里配了标签 → 用它;没配(新商家)→ 留空,不再兜底"人才专享"。 assertThat(service.configuredMerchantTag( "[{\"level\":\"卓越人才卡\",\"enabled\":true,\"tag\":\"卓越人才卡\"}]", "")) .isEqualTo("卓越人才卡"); assertThat(service.configuredMerchantTag("", "")).isEmpty(); assertThat(service.configuredMerchantTag("[]", "")).isEmpty(); // 权益里没写标签时退回商家服务标签。 assertThat(service.configuredMerchantTag("[{\"level\":\"卓越人才卡\",\"enabled\":true}]", "专属服务")).isEqualTo("专属服务"); } }