重构:优化订单通知消息标签,支持动态显示订单类型
- 新增 OrderMessageLabelResolver 用于解析订单场景标签 - 修改微信公众号下单通知,根据下单类型(随机单/指定单/打赏/礼物)显示对应标签 - 更新 WxCustomMpService 接口,传递 placeType 和 rewardType 参数 - 完善相关单元测试和 Mock 配置
This commit is contained in:
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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<WxMpTemplateMessage> 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<WxMpTemplateData> 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<WxMpTemplateMessage> 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("指定单");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user