From da2902c61c3dc2433e06a1ef362671163870c7c1 Mon Sep 17 00:00:00 2001 From: irving Date: Mon, 3 Nov 2025 22:51:48 -0500 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=EF=BC=9A=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E9=80=9A=E7=9F=A5=E6=B6=88=E6=81=AF=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=EF=BC=8C=E6=94=AF=E6=8C=81=E5=8A=A8=E6=80=81=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E8=AE=A2=E5=8D=95=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 OrderMessageLabelResolver 用于解析订单场景标签 - 修改微信公众号下单通知,根据下单类型(随机单/指定单/打赏/礼物)显示对应标签 - 更新 WxCustomMpService 接口,传递 placeType 和 rewardType 参数 - 完善相关单元测试和 Mock 配置 --- .../controller/PlayOrderInfoController.java | 20 +- .../admin/modules/order/job/OrderJob.java | 9 +- .../constant/OrderMessageLabelResolver.java | 42 +++++ .../controller/WxCustomController.java | 13 +- .../weichat/service/WxCustomMpService.java | 11 +- .../com/starry/admin/api/AbstractApiTest.java | 2 + .../admin/api/MockWxMpServiceConfig.java | 31 ++++ .../admin/api/WxCustomRandomOrderApiTest.java | 12 +- .../api/WxCustomSpecifiedOrderApiTest.java | 8 +- .../api/WxOrderInfoControllerApiTest.java | 9 +- .../service/WxCustomMpServiceTest.java | 173 ++++++++++++++++++ 11 files changed, 315 insertions(+), 15 deletions(-) create mode 100644 play-admin/src/main/java/com/starry/admin/modules/order/module/constant/OrderMessageLabelResolver.java create mode 100644 play-admin/src/test/java/com/starry/admin/api/MockWxMpServiceConfig.java create mode 100644 play-admin/src/test/java/com/starry/admin/modules/weichat/service/WxCustomMpServiceTest.java diff --git a/play-admin/src/main/java/com/starry/admin/modules/order/controller/PlayOrderInfoController.java b/play-admin/src/main/java/com/starry/admin/modules/order/controller/PlayOrderInfoController.java index fc8dbfb..48f9279 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/order/controller/PlayOrderInfoController.java +++ b/play-admin/src/main/java/com/starry/admin/modules/order/controller/PlayOrderInfoController.java @@ -76,7 +76,14 @@ public class PlayOrderInfoController { public R sendNotice(String orderId) { PlayOrderInfoEntity orderInfo = orderInfoService.selectOrderInfoById(orderId); List clerkList = clerkUserInfoService.list(Wrappers.lambdaQuery(PlayClerkUserInfoEntity.class).isNotNull(PlayClerkUserInfoEntity::getOpenid).eq(PlayClerkUserInfoEntity::getClerkState, "1").eq(PlayClerkUserInfoEntity::getSex, orderInfo.getSex())); - wxCustomMpService.sendCreateOrderMessageBatch(clerkList, orderInfo.getOrderNo(), orderInfo.getOrderMoney().toString(), orderInfo.getCommodityName(), orderId); + wxCustomMpService.sendCreateOrderMessageBatch( + clerkList, + orderInfo.getOrderNo(), + orderInfo.getOrderMoney().toString(), + orderInfo.getCommodityName(), + orderId, + orderInfo.getPlaceType(), + orderInfo.getRewardType()); return R.ok(); } @@ -126,8 +133,15 @@ public class PlayOrderInfoController { PlayOrderInfoEntity orderInfo = orderInfoService.selectOrderInfoById(vo.getOrderId()); PlayCommodityInfoVo commodityInfo = playCommodityInfoService.queryCommodityInfo(orderInfo.getCommodityId(), clerkUserInfo.getLevelId()); - wxCustomMpService.sendCreateOrderMessage(clerkUserInfo.getTenantId(), clerkUserInfo.getOpenid(), - orderInfo.getOrderNo(), orderInfo.getOrderMoney().toString(), commodityInfo.getCommodityName(), vo.getOrderId()); + wxCustomMpService.sendCreateOrderMessage( + clerkUserInfo.getTenantId(), + clerkUserInfo.getOpenid(), + orderInfo.getOrderNo(), + orderInfo.getOrderMoney().toString(), + commodityInfo.getCommodityName(), + vo.getOrderId(), + orderInfo.getPlaceType(), + orderInfo.getRewardType()); return R.ok("操作成功"); } diff --git a/play-admin/src/main/java/com/starry/admin/modules/order/job/OrderJob.java b/play-admin/src/main/java/com/starry/admin/modules/order/job/OrderJob.java index 9765753..ac26ddd 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/order/job/OrderJob.java +++ b/play-admin/src/main/java/com/starry/admin/modules/order/job/OrderJob.java @@ -96,7 +96,14 @@ public class OrderJob { redisTemplate.opsForValue().set("order_notice_" + orderInfo.getId(), "1", 30, java.util.concurrent.TimeUnit.MINUTES); List clerkList = clerkUserInfoService.list(Wrappers.lambdaQuery(PlayClerkUserInfoEntity.class).isNotNull(PlayClerkUserInfoEntity::getOpenid).eq(PlayClerkUserInfoEntity::getClerkState, "1") .eq(PlayClerkUserInfoEntity::getSex, orderInfo.getSex()).eq(PlayClerkUserInfoEntity::getTenantId, orderInfo.getTenantId())); - wxCustomMpService.sendCreateOrderMessageBatch(clerkList, orderInfo.getOrderNo(), orderInfo.getOrderMoney().toString(), orderInfo.getCommodityName(), orderInfo.getId()); + wxCustomMpService.sendCreateOrderMessageBatch( + clerkList, + orderInfo.getOrderNo(), + orderInfo.getOrderMoney().toString(), + orderInfo.getCommodityName(), + orderInfo.getId(), + orderInfo.getPlaceType(), + orderInfo.getRewardType()); } catch (Exception e) { log.error(e.getMessage(), e); diff --git a/play-admin/src/main/java/com/starry/admin/modules/order/module/constant/OrderMessageLabelResolver.java b/play-admin/src/main/java/com/starry/admin/modules/order/module/constant/OrderMessageLabelResolver.java new file mode 100644 index 0000000..311537e --- /dev/null +++ b/play-admin/src/main/java/com/starry/admin/modules/order/module/constant/OrderMessageLabelResolver.java @@ -0,0 +1,42 @@ +package com.starry.admin.modules.order.module.constant; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +/** + * Resolves human-readable labels for order scenarios used in notifications. + */ +@Slf4j +public final class OrderMessageLabelResolver { + + private OrderMessageLabelResolver() { + } + + public static String resolve(String placeTypeCode, String rewardTypeCode) { + OrderConstant.PlaceType placeTypeEnum = OrderConstant.PlaceType.RANDOM; + if (StringUtils.isNotBlank(placeTypeCode)) { + try { + placeTypeEnum = OrderConstant.PlaceType.fromCode(placeTypeCode); + } catch (IllegalArgumentException ex) { + log.warn("未知的下单类型,placeTypeCode={},按随机单处理。", placeTypeCode, ex); + } + } + switch (placeTypeEnum) { + case SPECIFIED: + return "指定单"; + case REWARD: + OrderConstant.RewardType rewardTypeEnum = OrderConstant.RewardType.BALANCE; + if (StringUtils.isNotBlank(rewardTypeCode)) { + try { + rewardTypeEnum = OrderConstant.RewardType.fromCode(rewardTypeCode); + } catch (IllegalArgumentException ex) { + log.warn("未知的打赏类型,rewardTypeCode={},按打赏处理。", rewardTypeCode, ex); + } + } + return rewardTypeEnum == OrderConstant.RewardType.GIFT ? "礼物" : "打赏"; + case RANDOM: + default: + return "随机单"; + } + } +} diff --git a/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxCustomController.java b/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxCustomController.java index 0331079..93b5e37 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxCustomController.java +++ b/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxCustomController.java @@ -355,7 +355,9 @@ public class WxCustomController { orderNo, netAmount.toString(), commodityInfo.getCommodityName(), - order.getId()); + order.getId(), + order.getPlaceType(), + order.getRewardType()); return R.ok("成功"); } @@ -430,7 +432,14 @@ public class WxCustomController { .eq(PlayClerkUserInfoEntity::getListingState, ListingStatus.LISTED.getCode()) .eq(PlayClerkUserInfoEntity::getOnlineState, "1") .eq(PlayClerkUserInfoEntity::getSex, vo.getSex())); - wxCustomMpService.sendCreateOrderMessageBatch(clerkList, orderNo, netAmount.toString(), commodityInfo.getCommodityName(),order.getId()); + wxCustomMpService.sendCreateOrderMessageBatch( + clerkList, + orderNo, + netAmount.toString(), + commodityInfo.getCommodityName(), + order.getId(), + order.getPlaceType(), + order.getRewardType()); // 记录订单,指定指定未接单后,进行退款处理 overdueOrderHandlerTask.enqueue(orderId + "_" + SecurityUtils.getTenantId()); // 下单成功后,先根据用户条件进行随机分配 diff --git a/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxCustomMpService.java b/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxCustomMpService.java index f5b2573..064650e 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxCustomMpService.java +++ b/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxCustomMpService.java @@ -17,6 +17,7 @@ import com.starry.admin.modules.clerk.module.enums.OnboardingStatus; import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService; import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity; import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService; +import com.starry.admin.modules.order.module.constant.OrderMessageLabelResolver; import com.starry.admin.modules.order.module.entity.PlayOrderComplaintInfoEntity; import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity; import com.starry.admin.modules.personnel.module.entity.PlayPersonnelAdminInfoEntity; @@ -149,7 +150,8 @@ public class WxCustomMpService { } } - public void sendCreateOrderMessageBatch(List clerkList, String orderNo, String string, String commodityName, String orderId) { + public void sendCreateOrderMessageBatch(List clerkList, String orderNo, String string, + String commodityName, String orderId, String placeType, String rewardType) { if (CollectionUtils.isEmpty(clerkList)) { return; } @@ -159,7 +161,7 @@ public class WxCustomMpService { .filter(ca -> OnboardingStatus.isActive(ca.getOnboardingState())) .filter(ca -> ListingStatus.isListed(ca.getListingState())) .forEach(ca -> sendCreateOrderMessage(ca.getTenantId(), ca.getOpenid(), orderNo, string, commodityName, - orderId))); + orderId, placeType, rewardType))); } /** @@ -173,7 +175,7 @@ public class WxCustomMpService { * @param orderId */ public void sendCreateOrderMessage(String tenantId, String openId, String orderNo, String money, - String commodityName, String orderId) { + String commodityName, String orderId, String placeType, String rewardType) { SysTenantEntity tenant = tenantService.selectSysTenantByTenantId(tenantId); WxMpTemplateMessage templateMessage = getWxMpTemplateMessage(tenant.getXindingdanshoulitongzhiTemplateId(), @@ -181,7 +183,7 @@ public class WxCustomMpService { List data = new ArrayList<>(); data.add(new WxMpTemplateData("time6", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"))); data.add(new WxMpTemplateData("character_string9", orderNo)); - data.add(new WxMpTemplateData("short_thing5", "陪聊下单")); + data.add(new WxMpTemplateData("short_thing5", OrderMessageLabelResolver.resolve(placeType, rewardType))); data.add(new WxMpTemplateData("thing11", commodityName)); data.add(new WxMpTemplateData("amount8", money)); templateMessage.setData(data); @@ -198,7 +200,6 @@ public class WxCustomMpService { } } - /** * 店员接单后,通过微信公众号发送消息 * diff --git a/play-admin/src/test/java/com/starry/admin/api/AbstractApiTest.java b/play-admin/src/test/java/com/starry/admin/api/AbstractApiTest.java index 1f5237e..2586a50 100644 --- a/play-admin/src/test/java/com/starry/admin/api/AbstractApiTest.java +++ b/play-admin/src/test/java/com/starry/admin/api/AbstractApiTest.java @@ -3,6 +3,7 @@ package com.starry.admin.api; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.MockMvc; @@ -11,6 +12,7 @@ import org.springframework.test.web.servlet.MockMvc; @AutoConfigureMockMvc @ActiveProfiles("apitest") @TestPropertySource(properties = "spring.task.scheduling.enabled=false") +@Import(MockWxMpServiceConfig.class) public abstract class AbstractApiTest { protected static final String TENANT_HEADER = "X-Tenant"; diff --git a/play-admin/src/test/java/com/starry/admin/api/MockWxMpServiceConfig.java b/play-admin/src/test/java/com/starry/admin/api/MockWxMpServiceConfig.java new file mode 100644 index 0000000..903fc5b --- /dev/null +++ b/play-admin/src/test/java/com/starry/admin/api/MockWxMpServiceConfig.java @@ -0,0 +1,31 @@ +package com.starry.admin.api; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.WxMpTemplateMsgService; +import org.mockito.Mockito; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.Profile; + +/** + * Provides stubbed WeChat MP services in the apitest profile so integration tests never hit external APIs. + */ +@TestConfiguration +@Profile("apitest") +public class MockWxMpServiceConfig { + + @Bean + @Primary + public WxMpService wxMpService() { + WxMpService service = mock(WxMpService.class, Mockito.RETURNS_DEEP_STUBS); + WxMpTemplateMsgService templateMsgService = mock(WxMpTemplateMsgService.class); + when(service.getTemplateMsgService()).thenReturn(templateMsgService); + when(service.switchoverTo(Mockito.anyString())).thenReturn(service); + when(service.switchover(Mockito.anyString())).thenReturn(true); + return service; + } +} diff --git a/play-admin/src/test/java/com/starry/admin/api/WxCustomRandomOrderApiTest.java b/play-admin/src/test/java/com/starry/admin/api/WxCustomRandomOrderApiTest.java index f582f63..f444ab9 100644 --- a/play-admin/src/test/java/com/starry/admin/api/WxCustomRandomOrderApiTest.java +++ b/play-admin/src/test/java/com/starry/admin/api/WxCustomRandomOrderApiTest.java @@ -397,6 +397,14 @@ class WxCustomRandomOrderApiTest extends WxCustomOrderApiTestSupport { Assertions.assertThat(order.getFinalAmount()).isEqualByComparingTo(expectedNet); Assertions.assertThat(order.getDiscountAmount()).isEqualByComparingTo(discount); + verify(wxCustomMpService).sendCreateOrderMessageBatch( + anyList(), + eq(order.getOrderNo()), + eq(expectedNet.toString()), + eq(order.getCommodityName()), + eq(order.getId()), + eq(order.getPlaceType()), + eq(order.getRewardType())); String orderId = order.getId(); PlayCouponDetailsEntity detailAfterOrderPlaced = couponDetailsService.getById(couponDetailId); @@ -667,7 +675,9 @@ class WxCustomRandomOrderApiTest extends WxCustomOrderApiTestSupport { eq(order.getOrderNo()), eq(order.getFinalAmount().toString()), eq(order.getCommodityName()), - eq(order.getId())); + eq(order.getId()), + eq(order.getPlaceType()), + eq(order.getRewardType())); verify(overdueOrderHandlerTask).enqueue(order.getId() + "_" + ApiTestDataSeeder.DEFAULT_TENANT_ID); reset(wxCustomMpService); diff --git a/play-admin/src/test/java/com/starry/admin/api/WxCustomSpecifiedOrderApiTest.java b/play-admin/src/test/java/com/starry/admin/api/WxCustomSpecifiedOrderApiTest.java index 5995cbf..02f00f5 100644 --- a/play-admin/src/test/java/com/starry/admin/api/WxCustomSpecifiedOrderApiTest.java +++ b/play-admin/src/test/java/com/starry/admin/api/WxCustomSpecifiedOrderApiTest.java @@ -244,7 +244,9 @@ class WxCustomSpecifiedOrderApiTest extends WxCustomOrderApiTestSupport { anyString(), eq(expectedNet.toString()), eq(order.getCommodityName()), - eq(order.getId())); + eq(order.getId()), + eq(order.getPlaceType()), + eq(order.getRewardType())); int ratio = order.getEstimatedRevenueRatio(); BigDecimal baseRevenue = grossAmount @@ -411,7 +413,9 @@ class WxCustomSpecifiedOrderApiTest extends WxCustomOrderApiTestSupport { anyString(), eq(order.getFinalAmount().toString()), eq(order.getCommodityName()), - eq(order.getId())); + eq(order.getId()), + eq(order.getPlaceType()), + eq(order.getRewardType())); ensureTenantContext(); long afterCount = playOrderInfoService.lambdaQuery() diff --git a/play-admin/src/test/java/com/starry/admin/api/WxOrderInfoControllerApiTest.java b/play-admin/src/test/java/com/starry/admin/api/WxOrderInfoControllerApiTest.java index 8d96e8a..7907813 100644 --- a/play-admin/src/test/java/com/starry/admin/api/WxOrderInfoControllerApiTest.java +++ b/play-admin/src/test/java/com/starry/admin/api/WxOrderInfoControllerApiTest.java @@ -67,7 +67,14 @@ class WxOrderInfoControllerApiTest extends WxCustomOrderApiTestSupport { // Relax notifications to avoid strict verification noise doNothing().when(notificationSender).sendOrderMessageAsync(Mockito.any()); doNothing().when(notificationSender).sendOrderFinishMessageAsync(Mockito.any()); - doNothing().when(wxCustomMpService).sendCreateOrderMessageBatch(anyList(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); + doNothing().when(wxCustomMpService).sendCreateOrderMessageBatch( + anyList(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString()); doNothing().when(overdueOrderHandlerTask).enqueue(Mockito.anyString()); } diff --git a/play-admin/src/test/java/com/starry/admin/modules/weichat/service/WxCustomMpServiceTest.java b/play-admin/src/test/java/com/starry/admin/modules/weichat/service/WxCustomMpServiceTest.java new file mode 100644 index 0000000..e95ef1b --- /dev/null +++ b/play-admin/src/test/java/com/starry/admin/modules/weichat/service/WxCustomMpServiceTest.java @@ -0,0 +1,173 @@ +package com.starry.admin.modules.weichat.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity; +import com.starry.admin.modules.clerk.module.enums.ListingStatus; +import com.starry.admin.modules.clerk.module.enums.OnboardingStatus; +import com.starry.admin.modules.order.module.constant.OrderConstant; +import com.starry.admin.modules.system.module.entity.SysTenantEntity; +import com.starry.admin.modules.system.service.impl.SysTenantServiceImpl; +import java.util.Arrays; +import java.util.List; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.WxMpTemplateMsgService; +import me.chanjar.weixin.mp.bean.template.WxMpTemplateData; +import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage; +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 org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +@ExtendWith(MockitoExtension.class) +class WxCustomMpServiceTest { + + private static final String TENANT_ID = "tenant-1"; + private static final String APP_ID = "wx-app"; + + @Mock + private WxMpService wxMpService; + + @Mock + private WxMpTemplateMsgService templateMsgService; + + @Mock + private SysTenantServiceImpl tenantService; + + @Mock + private com.starry.admin.modules.custom.service.IPlayCustomUserInfoService customUserInfoService; + + @Mock + private com.starry.admin.modules.personnel.service.IPlayPersonnelAdminInfoService playPersonnelAdminInfoService; + + @Mock + private com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService clerkUserInfoService; + + @Mock + private ThreadPoolTaskExecutor executor; + + @InjectMocks + private WxCustomMpService wxCustomMpService; + + private SysTenantEntity buildTenant() { + return new SysTenantEntity() + .setTenantId(TENANT_ID) + .setTenantKey("tenant-key") + .setAppId(APP_ID) + .setSecret("wx-secret") + .setXindingdanshoulitongzhiTemplateId("template-create-order"); + } + + @Test + void sendCreateOrderMessageUsesOrderLabelResolver() throws WxErrorException { + SysTenantEntity tenant = buildTenant(); + + when(tenantService.selectSysTenantByTenantId(TENANT_ID)).thenReturn(tenant); + when(wxMpService.switchoverTo(tenant.getAppId())).thenReturn(wxMpService); + when(wxMpService.getTemplateMsgService()).thenReturn(templateMsgService); + + PlayClerkUserInfoEntity clerkDetails = new PlayClerkUserInfoEntity(); + clerkDetails.setId("clerk-1"); + clerkDetails.setNickname("Lily"); + when(clerkUserInfoService.selectByOpenid("openid-clerk")).thenReturn(clerkDetails); + + wxCustomMpService.sendCreateOrderMessage( + TENANT_ID, + "openid-clerk", + "ORDER-001", + "88.88", + "伴聊套餐", + "order-1", + OrderConstant.PlaceType.REWARD.getCode(), + OrderConstant.RewardType.GIFT.getCode()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(WxMpTemplateMessage.class); + verify(templateMsgService).sendTemplateMsg(captor.capture()); + + WxMpTemplateMessage message = captor.getValue(); + assertThat(message.getTemplateId()).isEqualTo(tenant.getXindingdanshoulitongzhiTemplateId()); + assertThat(message.getToUser()).isEqualTo("openid-clerk"); + assertThat(message.getUrl()) + .isEqualTo("https://" + tenant.getTenantKey() + ".julyharbor.com/clerk/#/orderDetail/order-1"); + + List data = message.getData(); + assertThat(data) + .filteredOn(templateData -> "short_thing5".equals(templateData.getName())) + .singleElement() + .extracting(WxMpTemplateData::getValue) + .isEqualTo("礼物"); + assertThat(data) + .filteredOn(templateData -> "character_string9".equals(templateData.getName())) + .singleElement() + .extracting(WxMpTemplateData::getValue) + .isEqualTo("ORDER-001"); + } + + @Test + void sendCreateOrderMessageBatchFiltersInactiveClerksAndForwardsLabel() throws WxErrorException { + SysTenantEntity tenant = buildTenant(); + + when(tenantService.selectSysTenantByTenantId(TENANT_ID)).thenReturn(tenant); + when(wxMpService.switchoverTo(anyString())).thenReturn(wxMpService); + when(wxMpService.getTemplateMsgService()).thenReturn(templateMsgService); + + doAnswer(invocation -> { + Runnable runnable = invocation.getArgument(0); + runnable.run(); + return null; + }).when(executor).execute(any(Runnable.class)); + + PlayClerkUserInfoEntity eligible = new PlayClerkUserInfoEntity(); + eligible.setTenantId(TENANT_ID); + eligible.setOpenid("openid-eligible"); + eligible.setOnboardingState(OnboardingStatus.ACTIVE.getCode()); + eligible.setListingState(ListingStatus.LISTED.getCode()); + + PlayClerkUserInfoEntity offboarded = new PlayClerkUserInfoEntity(); + offboarded.setTenantId(TENANT_ID); + offboarded.setOpenid("openid-offboarded"); + offboarded.setOnboardingState(OnboardingStatus.OFFBOARDED.getCode()); + offboarded.setListingState(ListingStatus.LISTED.getCode()); + + PlayClerkUserInfoEntity delisted = new PlayClerkUserInfoEntity(); + delisted.setTenantId(TENANT_ID); + delisted.setOpenid("openid-delisted"); + delisted.setOnboardingState(OnboardingStatus.ACTIVE.getCode()); + delisted.setListingState(ListingStatus.DELISTED.getCode()); + + PlayClerkUserInfoEntity clerkDetails = new PlayClerkUserInfoEntity(); + clerkDetails.setId("clerk-eligible"); + clerkDetails.setNickname("Kara"); + when(clerkUserInfoService.selectByOpenid("openid-eligible")).thenReturn(clerkDetails); + + wxCustomMpService.sendCreateOrderMessageBatch( + Arrays.asList(eligible, offboarded, delisted, null), + "ORDER-002", + "45.00", + "单人畅聊", + "order-2", + OrderConstant.PlaceType.SPECIFIED.getCode(), + OrderConstant.RewardType.NOT_APPLICABLE.getCode()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(WxMpTemplateMessage.class); + verify(templateMsgService, times(1)).sendTemplateMsg(captor.capture()); + + WxMpTemplateMessage message = captor.getValue(); + assertThat(message.getToUser()).isEqualTo("openid-eligible"); + assertThat(message.getData()) + .filteredOn(templateData -> "short_thing5".equals(templateData.getName())) + .singleElement() + .extracting(WxMpTemplateData::getValue) + .isEqualTo("指定单"); + } +}