| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- package com.mangoo.rating.recommend.cart;
- import com.mangoo.rating.recommend.AjaxResult;
- import com.mangoo.rating.recommend.client.feign.PreOrderFeignClient;
- import com.mangoo.rating.recommend.config.RecommendProperties;
- import com.mangoo.rating.recommend.manager.ActiveBatchManager;
- import com.mangoo.rating.recommend.manager.RatingRecommendRuleManager;
- import com.mangoo.rating.recommend.po.RatingRecommendRulePO;
- import com.mangoo.rating.recommend.request.recommend.PreOrderCardDTO;
- import com.mangoo.rating.recommend.request.recommend.PreOrderSaveDTO;
- import com.mangoo.rating.recommend.request.recommend.RecommendCardDTO;
- import com.mangoo.rating.recommend.request.recommend.RecommendRequest;
- import com.mangoo.rating.recommend.service.impl.RecommendServiceImpl;
- import com.mangoo.rating.recommend.service.recommend.EfficiencyPriceProvider;
- import com.mangoo.rating.recommend.service.recommend.LoginUserProvider;
- import org.junit.jupiter.api.Test;
- import org.junit.jupiter.api.extension.ExtendWith;
- import org.mockito.ArgumentCaptor;
- import org.mockito.InjectMocks;
- import org.mockito.Mock;
- import org.mockito.junit.jupiter.MockitoExtension;
- import java.math.BigDecimal;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.HashMap;
- import java.util.Map;
- import static org.junit.jupiter.api.Assertions.assertEquals;
- import static org.junit.jupiter.api.Assertions.assertNotEquals;
- import static org.junit.jupiter.api.Assertions.assertTrue;
- import static org.mockito.ArgumentMatchers.any;
- import static org.mockito.ArgumentMatchers.anyString;
- import static org.mockito.Mockito.lenient;
- import static org.mockito.Mockito.never;
- import static org.mockito.Mockito.verify;
- import static org.mockito.Mockito.when;
- /**
- * RecommendServiceImpl 单测(预订单落库改造后:返回 preOrderNo + Feign 落库)
- *
- * 覆盖的目标场景:
- * 1) 空入参 / null → 非 0 code
- * 2) 超上限 → 非 0 code
- * 3) 正常,Feign 返回 preOrderNo → code=0,data=preOrderNo
- * 4) userId 为 null(未登录) → 非 0 code,且不调用 Feign
- * 5) Feign 抛异常 → 非 0 code
- * 6) Feign 返回 code!=0 → 非 0 code(下游 msg 不再透传,改为固定文案)
- * 7) 落库包字段映射:10 个识别字段 + userId/totalCards/packageId 全透传
- *
- * @author gengjintao
- */
- @ExtendWith(MockitoExtension.class)
- class RecommendServiceImplTest {
- @Mock
- private RatingRecommendRuleManager ratingRecommendRuleManager;
- @Mock
- private EfficiencyPriceProvider efficiencyPriceProvider;
- @Mock
- private RecommendProperties recommendProperties;
- @Mock
- private ActiveBatchManager activeBatchManager;
- @Mock
- private PreOrderFeignClient preOrderFeignClient;
- @Mock
- private LoginUserProvider loginUserProvider;
- @InjectMocks
- private RecommendServiceImpl recommendService;
- /** 落库失败对外统一文案(生产代码不再透传下游 msg) */
- private static final String FAIL_MSG = "预订单生成失败,请稍后重试";
- /** 造最简卡入参:仅带识别 4 字段 */
- private RecommendCardDTO card(String player, String year, String series, String cardSet) {
- RecommendCardDTO d = new RecommendCardDTO();
- d.setPlayer(player);
- d.setYear(year);
- d.setSeries(series);
- d.setCardSet(cardSet);
- return d;
- }
- /** 造一个 request(浅拷贝入参列表,避免测试之间共享同一 List) */
- private RecommendRequest req(RecommendCardDTO... cards) {
- RecommendRequest r = new RecommendRequest();
- r.setCards(new ArrayList<>(Arrays.asList(cards)));
- return r;
- }
- /**
- * 构造一条"合法且能通过 isLegalRule 校验"的 EXACT 命中规则。
- * 注意 evaluateEfficiencyName 在 PO 中是 Long(非 String),此处按真实签名给 3L。
- */
- private RatingRecommendRulePO exactRule() {
- RatingRecommendRulePO po = new RatingRecommendRulePO();
- po.setEvaluateEfficiencyId(3L);
- po.setEvaluateEfficiencyName(3L);
- po.setCardId("DW-CARD-1");
- return po;
- }
- /** 构造 Feign 成功返回:data 里带 preOrderNo */
- private AjaxResult okWith(String preOrderNo) {
- Map<String, Object> data = new HashMap<>();
- data.put("preOrderNo", preOrderNo);
- return AjaxResult.success(data);
- }
- /** 默认让匹配命中、价格可取、用户已登录(各用例可覆盖) */
- private void defaultHappyStubs() {
- lenient().when(recommendProperties.getBatchMax()).thenReturn(50);
- lenient().when(activeBatchManager.selectActiveBatchNo()).thenReturn("B1");
- lenient().when(ratingRecommendRuleManager.selectByExact(any(), any(), any(), any(), any()))
- .thenReturn(exactRule());
- Map<Long, BigDecimal> prices = new HashMap<>();
- prices.put(3L, new BigDecimal("200.00"));
- lenient().when(efficiencyPriceProvider.loadEfficiencyPrices()).thenReturn(prices);
- lenient().when(loginUserProvider.currentUserId()).thenReturn(1001L);
- lenient().when(loginUserProvider.currentUserHeader()).thenReturn("base64header");
- }
- private int code(AjaxResult r) {
- return r.getCode();
- }
- @Test
- void 空入参_返回error() {
- // null / 空列表:直接拦截,不调用 Feign
- assertNotEquals(0, code(recommendService.recommendEfficiency(null)));
- assertNotEquals(0, code(recommendService.recommendEfficiency(new RecommendRequest())));
- }
- @Test
- void 超限_返回error() {
- // 上限=1,传 2 张 → 拦截
- when(recommendProperties.getBatchMax()).thenReturn(1);
- AjaxResult result = recommendService.recommendEfficiency(
- req(card("p", "y", "s", "cs"), card("p", "y", "s", "cs")));
- assertNotEquals(0, code(result));
- }
- @Test
- void 正常_落库成功_返回preOrderNo() {
- defaultHappyStubs();
- when(preOrderFeignClient.savePreOrder(anyString(), any(PreOrderSaveDTO.class)))
- .thenReturn(okWith("PRE123456"));
- AjaxResult result = recommendService.recommendEfficiency(req(card("Jordan", "1986", "Fleer", "Base")));
- assertEquals(0, code(result));
- // 生产代码:成功时 data 为 String preOrderNo
- assertEquals("PRE123456", result.get("data"));
- }
- @Test
- void 用户未登录_不调用Feign_返回error() {
- defaultHappyStubs();
- // 覆盖 defaultHappyStubs 里的登录用户
- when(loginUserProvider.currentUserId()).thenReturn(null);
- AjaxResult result = recommendService.recommendEfficiency(req(card("p", "y", "s", "cs")));
- assertNotEquals(0, code(result));
- // 关键:未登录时不得触发远程调用
- verify(preOrderFeignClient, never()).savePreOrder(any(), any());
- }
- @Test
- void Feign抛异常_返回生成失败() {
- defaultHappyStubs();
- when(preOrderFeignClient.savePreOrder(anyString(), any(PreOrderSaveDTO.class)))
- .thenThrow(new RuntimeException("timeout"));
- AjaxResult result = recommendService.recommendEfficiency(req(card("p", "y", "s", "cs")));
- assertNotEquals(0, code(result));
- assertEquals(FAIL_MSG, result.getMsg());
- }
- @Test
- void Feign返回非0_透传错误() {
- defaultHappyStubs();
- // 下游返回 error(含具体 msg "下游库满"),生产代码经质量审查后已不再透传下游 msg,
- // 对外统一为固定文案 FAIL_MSG,避免下游信息泄露。故只断言 code 非 0 + 固定文案。
- when(preOrderFeignClient.savePreOrder(anyString(), any(PreOrderSaveDTO.class)))
- .thenReturn(AjaxResult.error("下游库满"));
- AjaxResult result = recommendService.recommendEfficiency(req(card("p", "y", "s", "cs")));
- assertNotEquals(0, code(result));
- assertEquals(FAIL_MSG, result.getMsg());
- }
- @Test
- void 落库包_识别字段与关键字段全部透传() {
- defaultHappyStubs();
- when(preOrderFeignClient.savePreOrder(anyString(), any(PreOrderSaveDTO.class)))
- .thenReturn(okWith("PRE999"));
- // 构造含 10 个补齐识别字段 + id/price 的入参卡
- RecommendCardDTO c = card("Jordan", "1986", "Fleer", "Base");
- c.setSportEvent("篮球");
- c.setCardType(2);
- c.setRole("SG");
- c.setCardNo("NO-1");
- c.setTeam("Bulls");
- c.setLimitId("/99");
- c.setIpName("NBA");
- c.setRule("MVP");
- c.setRarity("SSR");
- c.setMaterial("金箔");
- c.setId(88L);
- c.setPrice(new BigDecimal("120"));
- recommendService.recommendEfficiency(req(c));
- // 抓取实际传给 Feign 的 PreOrderSaveDTO
- ArgumentCaptor<PreOrderSaveDTO> captor = ArgumentCaptor.forClass(PreOrderSaveDTO.class);
- verify(preOrderFeignClient).savePreOrder(anyString(), captor.capture());
- PreOrderSaveDTO sent = captor.getValue();
- // 主表汇总字段
- assertEquals(1001L, sent.getUserId());
- assertEquals(1, sent.getTotalCards());
- // 卡片明细:10 识别字段全部透传(sportEvent/cardType/role/cardNo/team/limitId/ipName/rule/rarity/material)
- PreOrderCardDTO card = sent.getGroups().get(0).getCards().get(0);
- assertEquals("篮球", card.getSportEvent());
- assertEquals(2, card.getCardType());
- assertEquals("SG", card.getRole());
- assertEquals("NO-1", card.getCardNo());
- assertEquals("Bulls", card.getTeam());
- assertEquals("/99", card.getLimitId());
- assertEquals("NBA", card.getIpName());
- // 入参 rule -> VO roleName -> PreOrderCardDTO.rule(往返映射)
- assertEquals("MVP", card.getRule());
- assertEquals("SSR", card.getRarity());
- assertEquals("金箔", card.getMaterial());
- // 套餐/命中:入参 id -> packageId;规则 cardId -> matchedCardId
- assertEquals(88L, card.getPackageId());
- assertEquals("DW-CARD-1", card.getMatchedCardId());
- // 时效价可取到时应大于 0
- assertTrue(card.getEfficiencyPrice().compareTo(BigDecimal.ZERO) > 0);
- }
- }
|